From 019248e8bdfee33c076effcc6d1aec209105cccd Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 25 Oct 2026 21:12:50 +0200 Subject: [PATCH 01/32] Kotlin Multiplatform: Compose code as a Codename One app A KMP module's commonMain (and its JVM source set) builds as a Codename One application: kotlinc compiles it for the JVM with the Compose compiler plugin, the unmodified Compose runtime and kotlinx.coroutines ship as they are, and maven/compose-compat provides the UI layer over Codename One components. - compose-compat: Modifier chains, Box/Row/Column/Lazy layout policies, text, material3, animation; top and navigation bars pad by the part of the safe area their host lies under, read at measure time. - cn1:import-kmp-project, the archetype compose profile, Gradle support. - ParparVM: field updaters lowered at translate time, ServiceLoader registry, the JavaAPI classes the Compose runtime and coroutines need. - Theme loading no longer leaves 24k strings behind (ThemeStrings), and Font.derive finds a cached font without building a key. - hellocodenameone moves to Kotlin 2.2.10 and carries KmpCompat screenshot tests; scripts/kmp-bench compares the same source against Compose Multiplatform; new developer guide chapter with diagrams. Co-Authored-By: Claude Opus 5.5 --- .github/ci/apple-checks.json | 8 + .github/workflows/car-android.yml | 2 + .github/workflows/car-ios.yml | 2 + .github/workflows/health-android.yml | 2 + .github/workflows/kmp-compat.yml | 106 +++ .github/workflows/linux-build-run.yml | 2 + .github/workflows/parparvm-tests-windows.yml | 2 + .github/workflows/pr.yml | 9 +- .github/workflows/scripts-android.yml | 2 + .github/workflows/scripts-javascript.yml | 2 + .github/workflows/scripts-macos.yml | 2 + .github/workflows/windows-cross-build-run.yml | 2 + CLAUDE.md | 60 ++ CodenameOne/src/com/codename1/ui/Font.java | 44 +- .../src/com/codename1/ui/plaf/UIManager.java | 133 +++- .../src/com/codename1/ui/util/Resources.java | 184 +++-- .../com/codename1/ui/util/ThemeStrings.java | 267 +++++++ .../designer/PreviewInSimulator.java | 2 +- .../codename1/designer/ResourceEditorApp.java | 2 +- .../designer/ResourceEditorView.java | 2 +- .../impl/android/AndroidImplementation.java | 4 +- .../com/codename1/impl/javase/CSSWatcher.java | 2 +- .../impl/javase/ComponentTreeInspector.java | 2 +- .../com/codename1/impl/javase/JavaSEPort.java | 10 +- .../testing/junit/CodenameOneExtension.java | 6 +- .../impl/html5/HTML5Implementation.java | 4 +- .../impl/linux/LinuxImplementation.java | 2 +- .../impl/windows/WindowsImplementation.java | 2 +- .../codename1/impl/ios/IOSImplementation.java | 20 +- .../Kotlin-Multiplatform-Interop.asciidoc | 237 ++++++ docs/developer-guide/developer-guide.asciidoc | 2 + .../developer-guide/img/kmp-bench-results.svg | 31 + .../img/kmp-build-pipeline.svg | 77 ++ .../img/kmp-keynote-schedule.png | Bin 0 -> 302200 bytes .../img/kmp-runtime-layers.svg | 44 ++ .../com/codename1/builders/IPhoneBuilder.java | 9 + .../com/codename1/maven/GradleConversion.java | 29 +- .../codename1/maven/KmpProjectImporter.java | 308 ++++++++ .../java/com/codename1/maven/Relocation.java | 26 +- .../maven/KmpProjectImporterTest.java | 92 +++ .../archetype-resources/common/pom.xml | 69 +- .../resources/archetype-resources/pom.xml | 8 + .../java/com/codename1/gradle/AppSupport.java | 33 + .../com/codename1/gradle/SettingsSupport.java | 4 + .../codename1/maven/ImportKmpProjectMojo.java | 87 +++ maven/compose-compat/pom.xml | 204 +++++ maven/compose-compat/spotbugs-exclude.xml | 125 +++ .../compose/animation/AnimatedVisibility.kt | 68 ++ .../compose/animation/core/Animation.kt | 283 +++++++ .../androidx/compose/foundation/Foundation.kt | 155 ++++ .../compose/foundation/layout/Arrangement.kt | 112 +++ .../compose/foundation/layout/Layout.kt | 183 +++++ .../compose/foundation/lazy/LazyList.kt | 657 ++++++++++++++++ .../compose/foundation/shape/Shapes.kt | 86 +++ .../compose/foundation/text/BasicText.kt | 54 ++ .../androidx/compose/material/icons/Icons.kt | 38 + .../icons/automirrored/filled/AutoMirrored.kt | 39 + .../compose/material/icons/filled/Filled.kt | 150 ++++ .../material/icons/outlined/Outlined.kt | 150 ++++ .../androidx/compose/material3/ColorScheme.kt | 158 ++++ .../androidx/compose/material3/Components.kt | 411 ++++++++++ .../androidx/compose/material3/Structure.kt | 306 ++++++++ .../androidx/compose/material3/Theme.kt | 98 +++ .../androidx/compose/material3/Typography.kt | 49 ++ .../kotlin/androidx/compose/ui/Alignment.kt | 89 +++ .../kotlin/androidx/compose/ui/Modifier.kt | 76 ++ .../kotlin/androidx/compose/ui/draw/Draw.kt | 35 + .../androidx/compose/ui/geometry/Geometry.kt | 68 ++ .../androidx/compose/ui/graphics/Graphics.kt | 161 ++++ .../ui/graphics/drawscope/DrawScope.kt | 77 ++ .../compose/ui/graphics/vector/ImageVector.kt | 42 + .../compose/ui/layout/ContentScale.kt | 38 + .../androidx/compose/ui/platform/Platform.kt | 30 + .../androidx/compose/ui/text/TextStyle.kt | 69 ++ .../compose/ui/text/font/FontWeight.kt | 65 ++ .../compose/ui/text/style/TextStyles.kt | 55 ++ .../kotlin/androidx/compose/ui/unit/Units.kt | 130 ++++ .../kotlin/com/codename1/compose/Compose.kt | 90 +++ .../codename1/compose/internal/ComposeHost.kt | 277 +++++++ .../codename1/compose/internal/Constraints.kt | 123 +++ .../codename1/compose/internal/Dispatch.kt | 97 +++ .../codename1/compose/internal/Elements.kt | 110 +++ .../com/codename1/compose/internal/Emit.kt | 53 ++ .../com/codename1/compose/internal/Glyphs.kt | 30 + .../com/codename1/compose/internal/Leaves.kt | 347 +++++++++ .../codename1/compose/internal/Painting.kt | 272 +++++++ .../codename1/compose/internal/Policies.kt | 176 +++++ .../com/codename1/compose/internal/UiNode.kt | 718 ++++++++++++++++++ ....coroutines.internal.MainDispatcherFactory | 1 + .../com/codename1/compose/KeynoteAppTest.kt | 164 ++++ .../com/codename1/compose/LayoutTest.kt | 172 +++++ .../com/codename1/compose/LazyListTest.kt | 167 ++++ maven/core-unittests/spotbugs-exclude.xml | 21 + .../codename1/ui/util/ThemeStringsTest.java | 84 ++ .../codename1/ui/util/EditableResources.java | 6 +- maven/integration-tests/kmp-compat-test.sh | 95 +++ maven/pom.xml | 2 + scripts/check-cast-semantics.sh | 5 +- scripts/copyright-header-exclusions.txt | 1 + scripts/hellocodenameone/common/pom.xml | 53 +- .../tests/Cn1ssDeviceRunner.java | 9 + .../KmpCompatLayoutScreenshotTest.java | 39 + .../KmpCompatListScreenshotTest.java | 39 + .../KmpCompatStateScreenshotTest.java | 39 + .../KmpCompatTypographyScreenshotTest.java | 39 + .../examples/hellocodenameone/kmp/Screens.kt | 152 ++++ .../hellocodenameone/kmp/KmpScenes.kt | 35 + scripts/hellocodenameone/pom.xml | 9 + scripts/kmp-bench/README.md | 85 +++ scripts/kmp-bench/app/cmp/Info.plist | 14 + scripts/kmp-bench/app/cmp/build.gradle.kts | 28 + .../app/cmp/composeApp/build.gradle.kts | 67 ++ .../composeApp/src/desktopMain/kotlin/Main.kt | 37 + .../cmp/composeApp/src/iosMain/kotlin/Main.kt | 79 ++ scripts/kmp-bench/app/cmp/gradle.properties | 3 + scripts/kmp-bench/app/cmp/settings.gradle.kts | 38 + scripts/kmp-bench/app/cn1/Bench.java | 39 + scripts/kmp-bench/app/cn1/BenchEntry.kt | 38 + scripts/kmp-bench/app/prepare.sh | 59 ++ .../shared/commonMain/kotlin/keynote/App.kt | 299 ++++++++ .../shared/commonMain/kotlin/keynote/Data.kt | 113 +++ scripts/kmp-bench/run_bench.sh | 161 ++++ .../src/commonMain/kotlin/counter/App.kt | 65 ++ .../src/desktopMain/kotlin/counter/Main.kt | 33 + .../kotlin/counter/MainViewController.kt | 29 + .../counter/settings.gradle.kts | 2 + vm/ByteCodeTranslator/src/cn1_globals.m | 16 +- .../tools/translator/ByteCodeClass.java | 200 +++-- .../tools/translator/ByteCodeField.java | 7 + .../tools/translator/ByteCodeTranslator.java | 24 +- .../tools/translator/BytecodeMethod.java | 16 +- .../codename1/tools/translator/Parser.java | 31 +- .../tools/translator/RuntimeLowering.java | 559 ++++++++++++++ .../tools/translator/bytecodes/Invoke.java | 2 +- .../translator/bytecodes/TypeInstruction.java | 4 +- vm/JavaAPI/src/java/lang/Class.java | 50 ++ vm/JavaAPI/src/java/lang/ClassLoader.java | 15 + vm/JavaAPI/src/java/lang/ClassValue.java | 63 ++ .../src/java/lang/NoSuchFieldException.java | 33 + .../src/java/lang/NoSuchMethodException.java | 33 + vm/JavaAPI/src/java/lang/Thread.java | 25 + .../src/java/lang/ref/ReferenceQueue.java | 59 ++ .../src/java/lang/ref/WeakReference.java | 8 + .../src/java/lang/reflect/Constructor.java | 13 + vm/JavaAPI/src/java/lang/reflect/Field.java | 53 ++ .../java/lang/reflect/GenericDeclaration.java | 28 + vm/JavaAPI/src/java/lang/reflect/Method.java | 19 + .../src/java/lang/reflect/Modifier.java | 55 ++ vm/JavaAPI/src/java/net/URLStreamHandler.java | 30 + vm/JavaAPI/src/java/util/HashMap.java | 11 + vm/JavaAPI/src/java/util/Hashtable.java | 65 +- vm/JavaAPI/src/java/util/ServiceLoader.java | 84 ++ .../concurrent/atomic/AtomicIntegerArray.java | 122 +++ .../atomic/AtomicIntegerFieldUpdater.java | 102 +++ .../concurrent/atomic/AtomicLongArray.java | 122 +++ .../atomic/AtomicLongFieldUpdater.java | 102 +++ .../atomic/AtomicReferenceArray.java | 96 +++ .../atomic/AtomicReferenceFieldUpdater.java | 79 ++ .../util/concurrent/locks/LockSupport.java | 119 +++ vm/JavaAPI/src/java/util/jar/JarFile.java | 46 ++ .../cn1/orm-enhanced-dependencies.list | 0 vm/tests/pom.xml | 56 ++ .../BytecodeInstructionIntegrationTest.java | 4 +- .../KotlinComposeIntegrationTest.java | 219 ++++++ .../translator/ComposeCoroutinesApp.java | 28 + .../tools/translator/ComposeCoroutinesApp.kt | 145 ++++ 166 files changed, 13321 insertions(+), 240 deletions(-) create mode 100644 .github/workflows/kmp-compat.yml create mode 100644 CodenameOne/src/com/codename1/ui/util/ThemeStrings.java create mode 100644 docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc create mode 100644 docs/developer-guide/img/kmp-bench-results.svg create mode 100644 docs/developer-guide/img/kmp-build-pipeline.svg create mode 100644 docs/developer-guide/img/kmp-keynote-schedule.png create mode 100644 docs/developer-guide/img/kmp-runtime-layers.svg create mode 100644 maven/build-engine/src/main/java/com/codename1/maven/KmpProjectImporter.java create mode 100644 maven/build-engine/src/test/java/com/codename1/maven/KmpProjectImporterTest.java create mode 100644 maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/ImportKmpProjectMojo.java create mode 100644 maven/compose-compat/pom.xml create mode 100644 maven/compose-compat/spotbugs-exclude.xml create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/animation/AnimatedVisibility.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/animation/core/Animation.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/foundation/Foundation.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/foundation/layout/Arrangement.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/foundation/layout/Layout.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/foundation/lazy/LazyList.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/foundation/shape/Shapes.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/foundation/text/BasicText.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/Icons.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/automirrored/filled/AutoMirrored.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/filled/Filled.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/outlined/Outlined.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material3/ColorScheme.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material3/Components.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material3/Structure.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material3/Theme.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/material3/Typography.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/Alignment.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/Modifier.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/draw/Draw.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/geometry/Geometry.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/Graphics.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/layout/ContentScale.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/platform/Platform.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/TextStyle.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/font/FontWeight.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/style/TextStyles.kt create mode 100644 maven/compose-compat/src/main/kotlin/androidx/compose/ui/unit/Units.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/Compose.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Constraints.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Dispatch.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Elements.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Emit.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Glyphs.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Leaves.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Painting.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Policies.kt create mode 100644 maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt create mode 100644 maven/compose-compat/src/main/resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory create mode 100644 maven/compose-compat/src/test/kotlin/com/codename1/compose/KeynoteAppTest.kt create mode 100644 maven/compose-compat/src/test/kotlin/com/codename1/compose/LayoutTest.kt create mode 100644 maven/compose-compat/src/test/kotlin/com/codename1/compose/LazyListTest.kt create mode 100644 maven/core-unittests/src/test/java/com/codename1/ui/util/ThemeStringsTest.java create mode 100755 maven/integration-tests/kmp-compat-test.sh create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatLayoutScreenshotTest.java create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatListScreenshotTest.java create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatStateScreenshotTest.java create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatTypographyScreenshotTest.java create mode 100644 scripts/hellocodenameone/common/src/main/kmp/commonMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/Screens.kt create mode 100644 scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/KmpScenes.kt create mode 100644 scripts/kmp-bench/README.md create mode 100644 scripts/kmp-bench/app/cmp/Info.plist create mode 100644 scripts/kmp-bench/app/cmp/build.gradle.kts create mode 100644 scripts/kmp-bench/app/cmp/composeApp/build.gradle.kts create mode 100644 scripts/kmp-bench/app/cmp/composeApp/src/desktopMain/kotlin/Main.kt create mode 100644 scripts/kmp-bench/app/cmp/composeApp/src/iosMain/kotlin/Main.kt create mode 100644 scripts/kmp-bench/app/cmp/gradle.properties create mode 100644 scripts/kmp-bench/app/cmp/settings.gradle.kts create mode 100644 scripts/kmp-bench/app/cn1/Bench.java create mode 100644 scripts/kmp-bench/app/cn1/BenchEntry.kt create mode 100755 scripts/kmp-bench/app/prepare.sh create mode 100644 scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/App.kt create mode 100644 scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/Data.kt create mode 100755 scripts/kmp-bench/run_bench.sh create mode 100644 scripts/kmp-compat-samples/counter/composeApp/src/commonMain/kotlin/counter/App.kt create mode 100644 scripts/kmp-compat-samples/counter/composeApp/src/desktopMain/kotlin/counter/Main.kt create mode 100644 scripts/kmp-compat-samples/counter/composeApp/src/iosMain/kotlin/counter/MainViewController.kt create mode 100644 scripts/kmp-compat-samples/counter/settings.gradle.kts create mode 100644 vm/ByteCodeTranslator/src/com/codename1/tools/translator/RuntimeLowering.java create mode 100644 vm/JavaAPI/src/java/lang/ClassValue.java create mode 100644 vm/JavaAPI/src/java/lang/NoSuchFieldException.java create mode 100644 vm/JavaAPI/src/java/lang/NoSuchMethodException.java create mode 100644 vm/JavaAPI/src/java/lang/ref/ReferenceQueue.java create mode 100644 vm/JavaAPI/src/java/lang/reflect/Field.java create mode 100644 vm/JavaAPI/src/java/lang/reflect/GenericDeclaration.java create mode 100644 vm/JavaAPI/src/java/lang/reflect/Modifier.java create mode 100644 vm/JavaAPI/src/java/net/URLStreamHandler.java create mode 100644 vm/JavaAPI/src/java/util/ServiceLoader.java create mode 100644 vm/JavaAPI/src/java/util/concurrent/atomic/AtomicIntegerArray.java create mode 100644 vm/JavaAPI/src/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java create mode 100644 vm/JavaAPI/src/java/util/concurrent/atomic/AtomicLongArray.java create mode 100644 vm/JavaAPI/src/java/util/concurrent/atomic/AtomicLongFieldUpdater.java create mode 100644 vm/JavaAPI/src/java/util/concurrent/atomic/AtomicReferenceArray.java create mode 100644 vm/JavaAPI/src/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java create mode 100644 vm/JavaAPI/src/java/util/concurrent/locks/LockSupport.java create mode 100644 vm/JavaAPI/src/java/util/jar/JarFile.java create mode 100644 vm/backend/gen/META-INF/cn1/orm-enhanced-dependencies.list create mode 100644 vm/tests/src/test/java/com/codename1/tools/translator/KotlinComposeIntegrationTest.java create mode 100644 vm/tests/src/test/resources/com/codename1/tools/translator/ComposeCoroutinesApp.java create mode 100644 vm/tests/src/test/resources/com/codename1/tools/translator/ComposeCoroutinesApp.kt diff --git a/.github/ci/apple-checks.json b/.github/ci/apple-checks.json index ec63e4e97c1..a0a9f2895ca 100644 --- a/.github/ci/apple-checks.json +++ b/.github/ci/apple-checks.json @@ -48,6 +48,7 @@ "scripts/unity-compat-samples/gallery/**", "maven/cil-translator/**", "maven/unity-compat/**", + "maven/compose-compat/**", "maven/build-engine/src/main/java/com/codename1/maven/Unity*", "maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*", "scripts/unity-gallery-prebuilt.sh", @@ -102,6 +103,7 @@ "scripts/unity-compat-samples/gallery/**", "maven/cil-translator/**", "maven/unity-compat/**", + "maven/compose-compat/**", "maven/build-engine/src/main/java/com/codename1/maven/Unity*", "maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*", "scripts/unity-gallery-prebuilt.sh", @@ -145,6 +147,7 @@ "scripts/unity-compat-samples/gallery/**", "maven/cil-translator/**", "maven/unity-compat/**", + "maven/compose-compat/**", "maven/build-engine/src/main/java/com/codename1/maven/Unity*", "maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*", "scripts/unity-gallery-prebuilt.sh", @@ -186,6 +189,7 @@ "scripts/unity-compat-samples/gallery/**", "maven/cil-translator/**", "maven/unity-compat/**", + "maven/compose-compat/**", "maven/build-engine/src/main/java/com/codename1/maven/Unity*", "maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*", "scripts/unity-gallery-prebuilt.sh", @@ -214,6 +218,7 @@ "scripts/unity-compat-samples/gallery/**", "maven/cil-translator/**", "maven/unity-compat/**", + "maven/compose-compat/**", "maven/build-engine/src/main/java/com/codename1/maven/Unity*", "maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*", "scripts/unity-gallery-prebuilt.sh", @@ -240,6 +245,7 @@ "scripts/unity-compat-samples/gallery/**", "maven/cil-translator/**", "maven/unity-compat/**", + "maven/compose-compat/**", "maven/build-engine/src/main/java/com/codename1/maven/Unity*", "maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*", "scripts/unity-gallery-prebuilt.sh", @@ -284,6 +290,7 @@ "scripts/unity-compat-samples/gallery/**", "maven/cil-translator/**", "maven/unity-compat/**", + "maven/compose-compat/**", "maven/build-engine/src/main/java/com/codename1/maven/Unity*", "maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*", "scripts/unity-gallery-prebuilt.sh", @@ -320,6 +327,7 @@ "scripts/unity-compat-samples/gallery/**", "maven/cil-translator/**", "maven/unity-compat/**", + "maven/compose-compat/**", "maven/build-engine/src/main/java/com/codename1/maven/Unity*", "maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*", "scripts/unity-gallery-prebuilt.sh", diff --git a/.github/workflows/car-android.yml b/.github/workflows/car-android.yml index 33a19b2588e..e2a9533aba5 100644 --- a/.github/workflows/car-android.yml +++ b/.github/workflows/car-android.yml @@ -13,6 +13,7 @@ name: Car (Android Auto) integration - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -46,6 +47,7 @@ name: Car (Android Auto) integration - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/.github/workflows/car-ios.yml b/.github/workflows/car-ios.yml index caf083dcf16..fa8e248dc9b 100644 --- a/.github/workflows/car-ios.yml +++ b/.github/workflows/car-ios.yml @@ -14,6 +14,7 @@ name: Car (CarPlay) integration - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -37,6 +38,7 @@ name: Car (CarPlay) integration - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/.github/workflows/health-android.yml b/.github/workflows/health-android.yml index 3a4c7bca2db..7bf02cee531 100644 --- a/.github/workflows/health-android.yml +++ b/.github/workflows/health-android.yml @@ -20,6 +20,7 @@ name: Health (Health Connect) integration - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -45,6 +46,7 @@ name: Health (Health Connect) integration - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/.github/workflows/kmp-compat.yml b/.github/workflows/kmp-compat.yml new file mode 100644 index 00000000000..4353b14bb6e --- /dev/null +++ b/.github/workflows/kmp-compat.yml @@ -0,0 +1,106 @@ +name: Kotlin Multiplatform compatibility + +# Compose Multiplatform code built as a Codename One application: the Compose UI +# layer written over Codename One (maven/compose-compat, which carries the real +# Compose runtime and kotlinx.coroutines as they are), the headless harness its +# tests run on (maven/compat-testing) and the build steps that import and +# package it (build-engine, the Maven and Gradle plugins, the archetype's +# compose profile). +# +# unit tests layout, recomposition, text and input, and the +# benchmark's Keynote application driven screen by +# screen, headless; JDK 17, as the Compose runtime's +# inline functions are JVM 11 bytecode +# kmp-compat-test.sh every sample under scripts/kmp-compat-samples +# imported unmodified, built by Maven and Gradle with +# the Compose compiler, and staged for iOS, Android, +# JavaScript and the desktop with identical uploads +# from both tools +# +# No display anywhere: the CSS compiler is headless, and so is every test. +on: + pull_request: + branches: [ master ] + paths: + - 'maven/compose-compat/**' + - 'maven/compat-testing/**' + - 'maven/build-engine/**' + - 'maven/codenameone-maven-plugin/**' + - 'maven/codenameone-gradle-plugin/**' + - 'maven/project-model/**' + - 'maven/cn1app-archetype/**' + - 'maven/pom.xml' + - 'maven/integration-tests/kmp-compat-test.sh' + - 'maven/integration-tests/inc/**' + - 'scripts/kmp-compat-samples/**' + # The unit tests drive the benchmark's shared application. + - 'scripts/kmp-bench/app/shared/**' + - '.github/workflows/kmp-compat.yml' + push: + branches: [ master ] + paths: + - 'maven/compose-compat/**' + - 'maven/compat-testing/**' + - 'maven/build-engine/**' + - 'maven/codenameone-maven-plugin/**' + - 'maven/codenameone-gradle-plugin/**' + - 'maven/project-model/**' + - 'maven/cn1app-archetype/**' + - 'maven/pom.xml' + - 'maven/integration-tests/kmp-compat-test.sh' + - 'maven/integration-tests/inc/**' + - 'scripts/kmp-compat-samples/**' + - 'scripts/kmp-bench/app/shared/**' + - '.github/workflows/kmp-compat.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + kmp-compat: + runs-on: ubuntu-24.04 + timeout-minutes: 60 + env: + CN1_GRADLE_REQUIRED: '1' + steps: + - uses: actions/checkout@v6 + # Installed FIRST so the JDK 8 below is the one JAVA_HOME names. + - name: Set up JDK 17 for Gradle and the Compose tests + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + - run: echo "GRADLE_JAVA_HOME=$JAVA_HOME" >> $GITHUB_ENV + - name: Set up JDK 8 + uses: actions/setup-java@v5 + with: + java-version: '8' + distribution: 'temurin' + cache: 'maven' + - name: Build Codename One Maven artifacts (includes archetypes and both plugins) + env: + RETRY_ONLY_MATCHING: transient + RETRY_ATTEMPTS: '5' + RETRY_DELAY_SECONDS: '60' + run: | + cd maven + bash ../scripts/ci/retry.sh mvn install -Plocal-dev-javase -DskipTests -Darchetype.test.skip=true -Dmaven.javadoc.skip=true + - name: Unit tests + working-directory: maven + run: JAVA_HOME="$GRADLE_JAVA_HOME" mvn -B -Plocal-dev-javase -pl compose-compat test + - name: Refresh archetype catalog + run: | + cd maven + mvn archetype:update-local-catalog -Plocal-dev-javase + mvn archetype:crawl -Plocal-dev-javase + - name: Kotlin Multiplatform samples, imported and built by Maven and Gradle + working-directory: maven/integration-tests + run: bash kmp-compat-test.sh + - name: Upload the test projects' logs + if: failure() + uses: actions/upload-artifact@v7 + with: + name: kmp-compat-logs + path: maven/integration-tests/build/kmp-compat/**/*.log + if-no-files-found: ignore diff --git a/.github/workflows/linux-build-run.yml b/.github/workflows/linux-build-run.yml index ada0e96fdd6..28824856a78 100644 --- a/.github/workflows/linux-build-run.yml +++ b/.github/workflows/linux-build-run.yml @@ -37,6 +37,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -76,6 +77,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/.github/workflows/parparvm-tests-windows.yml b/.github/workflows/parparvm-tests-windows.yml index 63893065026..fe6ff75f4fa 100644 --- a/.github/workflows/parparvm-tests-windows.yml +++ b/.github/workflows/parparvm-tests-windows.yml @@ -30,6 +30,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -63,6 +64,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 194431d2822..c84cee7ede9 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -402,7 +402,7 @@ jobs: -Dcn1.binaries="${CN1_BINARIES}" \ -Pcompile-android \ -DbackendAnalysis \ - -pl android,ios,project-model,build-engine,codenameone-maven-plugin,codenameone-gradle-plugin,backend,backend-test,backend-parparvm,javac,android-res-compiler,android-compat,swing-compat,javafx-compat,fxml-compiler,desktop-compat-runtime -am verify + -pl android,ios,project-model,build-engine,codenameone-maven-plugin,codenameone-gradle-plugin,backend,backend-test,backend-parparvm,javac,android-res-compiler,android-compat,swing-compat,javafx-compat,compose-compat,fxml-compiler,desktop-compat-runtime -am verify - name: Type-check the SQLCipher-backed database package if: ${{ matrix.java-version == 8 }} env: @@ -620,7 +620,7 @@ jobs: - name: Generate static analysis HTML summaries if: ${{ !cancelled() && matrix.java-version == 8 }} env: - QUALITY_REPORT_TARGET_DIRS: maven/core-unittests/target:maven/android/target:maven/ios/target:vm/ByteCodeTranslator/target:maven/codenameone-maven-plugin/target:maven/build-engine/target:maven/project-model/target:maven/codenameone-gradle-plugin/target:maven/build-hint-catalog/target:maven/build-hint-tools/target:maven/backend/target:maven/backend-test/target:maven/backend-parparvm/target:maven/javac/target:maven/android-res-compiler/target:maven/android-compat/target:maven/swing-compat/target:maven/javafx-compat/target:maven/fxml-compiler/target:maven/desktop-compat-runtime/target + QUALITY_REPORT_TARGET_DIRS: maven/core-unittests/target:maven/android/target:maven/ios/target:vm/ByteCodeTranslator/target:maven/codenameone-maven-plugin/target:maven/build-engine/target:maven/project-model/target:maven/codenameone-gradle-plugin/target:maven/build-hint-catalog/target:maven/build-hint-tools/target:maven/backend/target:maven/backend-test/target:maven/backend-parparvm/target:maven/javac/target:maven/android-res-compiler/target:maven/android-compat/target:maven/swing-compat/target:maven/javafx-compat/target:maven/compose-compat/target:maven/fxml-compiler/target:maven/desktop-compat-runtime/target QUALITY_REPORT_SERVER_URL: ${{ github.server_url }} QUALITY_REPORT_REPOSITORY: ${{ github.repository }} QUALITY_REPORT_REF: ${{ github.event.pull_request.head.sha || github.sha }} @@ -644,6 +644,7 @@ jobs: "android-compat:maven/android-compat/target" \ "swing-compat:maven/swing-compat/target" \ "javafx-compat:maven/javafx-compat/target" \ + "compose-compat:maven/compose-compat/target" \ "fxml-compiler:maven/fxml-compiler/target" \ "desktop-compat-runtime:maven/desktop-compat-runtime/target" \ "project-model:maven/project-model/target" \ @@ -776,11 +777,11 @@ jobs: - name: Generate quality report summary if: ${{ !cancelled() && matrix.java-version == 8 }} env: - QUALITY_REPORT_TARGET_DIRS: maven/core-unittests/target:maven/android/target:maven/ios/target:vm/ByteCodeTranslator/target:maven/codenameone-maven-plugin/target:maven/build-engine/target:maven/project-model/target:maven/codenameone-gradle-plugin/target:maven/build-hint-catalog/target:maven/build-hint-tools/target:maven/backend/target:maven/backend-test/target:maven/backend-parparvm/target:maven/javac/target:maven/android-res-compiler/target:maven/android-compat/target:maven/swing-compat/target:maven/javafx-compat/target:maven/fxml-compiler/target:maven/desktop-compat-runtime/target + QUALITY_REPORT_TARGET_DIRS: maven/core-unittests/target:maven/android/target:maven/ios/target:vm/ByteCodeTranslator/target:maven/codenameone-maven-plugin/target:maven/build-engine/target:maven/project-model/target:maven/codenameone-gradle-plugin/target:maven/build-hint-catalog/target:maven/build-hint-tools/target:maven/backend/target:maven/backend-test/target:maven/backend-parparvm/target:maven/javac/target:maven/android-res-compiler/target:maven/android-compat/target:maven/swing-compat/target:maven/javafx-compat/target:maven/compose-compat/target:maven/fxml-compiler/target:maven/desktop-compat-runtime/target # Every project listed here must produce a SpotBugs report and that # report must be empty. A missing report fails the gate too, so a # SpotBugs run that silently stops running is not mistaken for clean. - QUALITY_REPORT_REQUIRED_SPOTBUGS: core-unittests:android:ios:ByteCodeTranslator:codenameone-maven-plugin:build-engine:project-model:codenameone-gradle-plugin:build-hint-catalog:build-hint-tools:backend:backend-test:javac:android-res-compiler:android-compat:swing-compat:javafx-compat:fxml-compiler:desktop-compat-runtime + QUALITY_REPORT_REQUIRED_SPOTBUGS: core-unittests:android:ios:ByteCodeTranslator:codenameone-maven-plugin:build-engine:project-model:codenameone-gradle-plugin:build-hint-catalog:build-hint-tools:backend:backend-test:javac:android-res-compiler:android-compat:swing-compat:javafx-compat:compose-compat:fxml-compiler:desktop-compat-runtime # PMD and Checkstyle run on the core and on the backend runtime, and each # must produce its report: without one, the forbidden-rule gate below it # has nothing to read and passes. diff --git a/.github/workflows/scripts-android.yml b/.github/workflows/scripts-android.yml index 3726c551738..cb9970f659d 100644 --- a/.github/workflows/scripts-android.yml +++ b/.github/workflows/scripts-android.yml @@ -15,6 +15,7 @@ name: Test Android build scripts - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -72,6 +73,7 @@ name: Test Android build scripts - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/.github/workflows/scripts-javascript.yml b/.github/workflows/scripts-javascript.yml index 571c51dd780..65d3d66239a 100644 --- a/.github/workflows/scripts-javascript.yml +++ b/.github/workflows/scripts-javascript.yml @@ -20,6 +20,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -76,6 +77,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/.github/workflows/scripts-macos.yml b/.github/workflows/scripts-macos.yml index efd1bdec976..988651ebeec 100644 --- a/.github/workflows/scripts-macos.yml +++ b/.github/workflows/scripts-macos.yml @@ -27,6 +27,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -91,6 +92,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/.github/workflows/windows-cross-build-run.yml b/.github/workflows/windows-cross-build-run.yml index e5ca2637355..4f85e5fe98c 100644 --- a/.github/workflows/windows-cross-build-run.yml +++ b/.github/workflows/windows-cross-build-run.yml @@ -31,6 +31,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' @@ -59,6 +60,7 @@ on: - 'scripts/unity-compat-samples/gallery/**' - 'maven/cil-translator/**' - 'maven/unity-compat/**' + - 'maven/compose-compat/**' - 'maven/build-engine/src/main/java/com/codename1/maven/Unity*' - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*' - 'scripts/unity-gallery-prebuilt.sh' diff --git a/CLAUDE.md b/CLAUDE.md index d5d36659cb7..d56daabfd8b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1294,6 +1294,66 @@ Traps that have already cost a fix: port turns Space and Enter into the same key code, and drops every Ctrl/Alt/Meta combination before the form sees it. +### Kotlin Multiplatform: Compose code as a Codename One app + +A KMP module's `commonMain` (and its JVM source set, minus the file that opens a +desktop window) dropped into `common/src/main/kmp` -- or imported with +`cn1:import-kmp-project` (`KmpProjectImporter`) -- builds as a Codename One +application. There is no transpiler: kotlinc compiles it for the JVM, with the +Compose compiler plugin, and ParparVM translates the classes like any others. + +- **The Compose runtime and `kotlinx.coroutines` ship as they are.** Only the + UI layer is ours: `maven/compose-compat` is `androidx.compose.{ui,foundation, + material3,animation}` written over Codename One components and `Graphics`, + authored under the real names. `androidx/compose/` is **never relocated** + (`Relocation.isCompose`), not even when the Android layer is active: the + runtime the application was compiled against is the one it runs. +- **Measuring is Compose's, not Codename One's.** `UiNode` is a `Container` + whose layout walks the modifier chain outermost first under Compose + constraints (`BoxPolicy`, `LinearPolicy`, `LeafPolicy`); the Codename One + layout pass only copies the result out. `ComposeHost` runs the `Recomposer` + with a `BroadcastFrameClock` fed by `Form.registerAnimated`. +- **`Dispatchers.Main` is the EDT**, found by kotlinx.coroutines through + `META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory` + (`EdtDispatcherFactory`). The translator compiles that file into a registry; + there is no service loader on the device. +- **JVM 11, not 8.** The Compose runtime's inline functions are JVM 11 + bytecode, so the archetype's `compose` profile (active when `src/main/kmp` + exists) sets kotlinc's `jvmTarget` and javac's source/target to 11. The + compiler plugin version must equal the Kotlin version. +- **Gradle**: the plugin adds compose-compat and the kmp source roots, and + refuses the build, naming the line to add, unless + `org.jetbrains.kotlin.plugin.compose` is applied; `convert-to-gradle` writes + it. The settings plugin adds Google's repository, limited to `androidx.*`. + +Traps that have already cost a fix: + +- **The runtime's own dependencies are in Gradle metadata only.** + `runtime-desktop`'s pom does not name `androidx.collection`, so Maven + resolved the runtime without it, every JVM test passed, and the Xcode build + failed on a missing `androidx_collection_ScatterSet.h`. compose-compat + declares them itself. +- **A public inline function cannot call internal code**, and the Compose + layer's internals are internal: `Box`, `Row` and `Column` are not inline + here, as they are upstream. +- **Tests need JDK 17** (class version 55); on JDK 8 the module builds and + the `jdk8-no-tests` profile skips them. SpotBugs still runs on the JDK 8 leg. +- **`--` ends an XML comment.** A `spotbugs-exclude.xml` with one in a comment + is rejected without an error, and every finding comes back. +- **`LazyColumn`/`LazyRow` compose only what is in view** (`LazyListNode`): + every item is a composition of its own under the list's, made within a + quarter view of the window and disposed of half a view past it. Sizes of + items never measured are estimated from the average, and every pass anchors + on the first visible item, so learning a size above the view cannot move + what is on screen. `scrollToItem` repeats until the target's real position + holds. `reverseLayout = true` throws; it is not implemented. + +`maven/integration-tests/kmp-compat-test.sh` imports every sample under +`scripts/kmp-compat-samples`, builds it with Maven and Gradle and stages every +target's upload (`.github/workflows/kmp-compat.yml`). `scripts/kmp-bench` runs +the same Compose source against Compose Multiplatform on an iOS simulator; see +its README for the numbers and what they do not show. + ### Integration Tests Located in `maven/integration-tests/`: diff --git a/CodenameOne/src/com/codename1/ui/Font.java b/CodenameOne/src/com/codename1/ui/Font.java index d2251579f8c..15359aa963d 100644 --- a/CodenameOne/src/com/codename1/ui/Font.java +++ b/CodenameOne/src/com/codename1/ui/Font.java @@ -153,6 +153,11 @@ public class Font extends CN { private static final Hashtable bitmapCache = new Hashtable(); private static final HashMap derivedFontCache = new HashMap(); + /// What [#derive(float, int)] made, per unique id. A hit costs a map lookup + /// and a short scan, and allocates nothing: a String key of id, size and + /// weight made three objects per call even when the font was already there, + /// and a theme load derives every font of every style. + private static final HashMap derivedBySize = new HashMap(); /// Internal theme hook: inherit only native UI fonts, never icon or bundled fonts. public Font deriveNativeThemeFont(String family, float size) { @@ -183,12 +188,14 @@ public Font deriveNativeThemeFont(String family, float size) { /// constants instead of returning a stale pre-theme paint. public static void clearDerivedFontCache() { derivedFontCache.clear(); + derivedBySize.clear(); } private static Font defaultFont = new Font(null); private static boolean enableBitmapFont = true; private static float fontReturnedHeight; private Object font; private boolean ttf; + private int derivedWeight; private float pixelSize = -1; // for derived fonts only, the size that was requested private String fontUniqueId; @@ -603,16 +610,39 @@ public Font deriveLetterSpacing(float letterSpacing) { public Font derive(float sizePixels, int weight) { if (fontUniqueId != null) { // derive should recycle instances of Font to allow smarter caching and logic on the native side of the fence + Font[] derived = derivedBySize.get(fontUniqueId); + int count = 0; + if (derived != null) { + int bits = Float.floatToIntBits(sizePixels); + for (; count < derived.length && derived[count] != null; count++) { + Font d = derived[count]; + if (d.derivedWeight == weight && Float.floatToIntBits(d.pixelSize) == bits) { + return d; + } + } + } + // A miss falls back to the shared cache, whose keys createTrueTypeFont + // also writes: a derive of a font it made, at that font's own size, + // answers that font rather than a second native one, and the reverse. String key = fontUniqueId + "_" + sizePixels + "_" + weight; Font f = derivedFontCache.get(key); - if (f != null) { - return f; + if (f == null) { + f = new Font(Display.impl.deriveTrueTypeFont(font, sizePixels, weight)); + f.pixelSize = sizePixels; + f.fontUniqueId = fontUniqueId; + f.ttf = true; + derivedFontCache.put(key, f); + } + f.derivedWeight = weight; + if (derived == null || count == derived.length) { + Font[] grown = new Font[derived == null ? 4 : derived.length * 2]; + if (derived != null) { + System.arraycopy(derived, 0, grown, 0, derived.length); + } + derived = grown; + derivedBySize.put(fontUniqueId, derived); } - f = new Font(Display.impl.deriveTrueTypeFont(font, sizePixels, weight)); - f.pixelSize = sizePixels; - f.fontUniqueId = fontUniqueId; - f.ttf = true; - derivedFontCache.put(key, f); + derived[count] = f; return f; } else { // not sure if this ever happens but don't want to break that code diff --git a/CodenameOne/src/com/codename1/ui/plaf/UIManager.java b/CodenameOne/src/com/codename1/ui/plaf/UIManager.java index 7321dee6a9a..f72ec581647 100644 --- a/CodenameOne/src/com/codename1/ui/plaf/UIManager.java +++ b/CodenameOne/src/com/codename1/ui/plaf/UIManager.java @@ -289,7 +289,7 @@ private void restoreNativeBorderColors() { nativeBorderDerived.clear(); } - private void recordNativeThemeOwnership(Hashtable incoming) { + private void recordNativeThemeOwnership(HashMap incoming) { if (refreshingTheme) { return; } @@ -303,14 +303,14 @@ private void recordNativeThemeOwnership(Hashtable incoming) { nativeBaseFontSize = nativeFontSize((Font) base); } } - for (Object item : incoming.keySet()) { - String key = (String) item; + for (Map.Entry e : incoming.entrySet()) { + String key = e.getKey(); if (key.startsWith("@")) { if (!nativeLayer) { applicationConstants.add(key.substring(1)); } } else if (nativeLayer) { - if (incoming.get(key) instanceof Font) { + if (e.getValue() instanceof Font) { nativeFontKeys.add(key); } if (key.endsWith("border")) { @@ -566,7 +566,7 @@ private static Font parseFont(String fontStr) { /// Resources theme = null; /// try { /// theme = Resources.openLayered(resourceFile); - /// UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()[0])); + /// UIManager.getInstance().setThemeMap(theme.getThemeMap(theme.getThemeResourceNames()[0])); /// } catch (IOException e) { /// Log.e(e); /// } @@ -582,7 +582,7 @@ private static Font parseFont(String fontStr) { public static Resources initFirstTheme(String resourceFile) { try { Resources theme = Resources.openLayered(resourceFile); - UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()[0])); + UIManager.getInstance().setThemeMap(theme.getThemeMap(theme.getThemeResourceNames()[0])); Resources.setGlobalResources(theme); return theme; } catch (IOException e) { @@ -605,7 +605,7 @@ public static Resources initFirstTheme(String resourceFile) { public static Resources initNamedTheme(String resourceFile, String resName) { try { Resources theme = Resources.openLayered(resourceFile); - UIManager.getInstance().setThemeProps(theme.getTheme(resName)); + UIManager.getInstance().setThemeMap(theme.getThemeMap(resName)); Resources.setGlobalResources(theme); return theme; } catch (IOException e) { @@ -1113,11 +1113,46 @@ HashMap getThemeProps() { /// /// - `themeProps`: the properties of the given theme public void setThemeProps(Hashtable themeProps) { + setThemePropsFrom(themeMap(themeProps), themeProps); + } + + /// Allows manual theme loading from a map of key/value pairs. Prefer this to + /// [#setThemeProps(Hashtable)]: a `HashMap` is what the theme is built from, + /// so nothing is copied, and its loops compile to direct table walks with + /// no iterator and no locking. + /// + /// #### Parameters + /// + /// - `themeProps`: the properties of the given theme + public void setThemeMap(HashMap themeProps) { + setThemePropsFrom(themeProps, themeProps); + } + + /// A legacy theme table as the map the theme is built from. Keys that are + /// not strings are not theme entries and are left out. + private static HashMap themeMap(Hashtable themeProps) { + if (themeProps == null) { + return null; + } + HashMap map = new HashMap(themeProps.size() * 2); + Enumeration e = themeProps.keys(); + while (e.hasMoreElements()) { + Object key = e.nextElement(); + if (key instanceof String) { + map.put((String) key, themeProps.get(key)); + } + } + return map; + } + + /// `source` is what the theme listeners are handed: the table the caller + /// passed, so a listener written against the legacy API still sees one. + private void setThemePropsFrom(HashMap themeProps, Object source) { // Replacing the whole theme supersedes a deferred base theme: it would be // reset away here before anything saw it. See setDeferredBaseTheme. deferredBaseTheme = null; if (accessible) { - setThemePropsImpl(themeProps); + setThemePropsImpl(themeProps, source); } wasThemeInstalled = true; } @@ -1129,7 +1164,7 @@ public void setThemeProps(Hashtable themeProps) { /// - `installedTheme`: @param installedTheme the theme to be installed or null, this is used /// to check if style inheritance is used in which case we must NOT init /// style defaults for that particular component - private void resetThemeProps(Hashtable installedTheme) { + private void resetThemeProps(HashMap installedTheme) { themeProps = new HashMap(); wasThemeInstalled = false; if (current == null) { @@ -2064,6 +2099,17 @@ private void resetThemeProps(Hashtable installedTheme) { /// /// - `themeProps`: the properties of the given theme public void addThemeProps(Hashtable themeProps) { + addThemeMap(themeMap(themeProps)); + } + + /// Adds the given theme properties on top of the existing properties without + /// clearing the existing theme first. Prefer this to [#addThemeProps(Hashtable)], + /// which copies the table into a map first. + /// + /// #### Parameters + /// + /// - `themeProps`: the properties of the given theme + public void addThemeMap(HashMap themeProps) { // Layered ON the base theme, so a deferred one has to be installed first. ensureBaseTheme(); if (accessible) { @@ -2098,17 +2144,11 @@ public void addThemeProps(Hashtable themeProps) { /// staying in place). For each style key being re-set by the incoming /// load that does NOT re-assert its binding, drop the matching binding /// from `themeConstants` so the new literal wins. - private void dropSupersededBindings(Hashtable themeProps) { + private void dropSupersededBindings(HashMap themeProps) { if (themeProps == null || themeConstants == null || themeConstants.isEmpty()) { return; } - Enumeration e = themeProps.keys(); - while (e.hasMoreElements()) { - Object keyObj = e.nextElement(); - if (!(keyObj instanceof String)) { - continue; - } - String key = (String) keyObj; + for (String key : themeProps.keySet()) { if (key.startsWith("@")) { continue; } @@ -2232,10 +2272,7 @@ public void refreshTheme() { } restoreLargerTextFonts(); restoreNativeThemeValues(); - Hashtable props = new Hashtable(); - for (Map.Entry e : themeProps.entrySet()) { - props.put(e.getKey(), e.getValue()); - } + HashMap props = new HashMap(themeProps); // buildTheme strips `@`-prefixed constants into themeConstants and // drops them from the main themeProps map. Round-tripping through // setThemePropsImpl would therefore lose every constant - so @@ -2248,7 +2285,7 @@ public void refreshTheme() { } refreshingTheme = true; try { - setThemePropsImpl(props); + setThemePropsImpl(props, props); } finally { refreshingTheme = false; } @@ -2377,7 +2414,12 @@ public Object getThemeMaskConstant(String constantName) { return o; } + // for internal use by the resource editor, which installs a theme it is editing void setThemePropsImpl(Hashtable themeProps) { + setThemePropsImpl(themeMap(themeProps), themeProps); + } + + void setThemePropsImpl(HashMap themeProps, Object source) { if (!refreshingTheme && buildThemeDepth == 0) { restoreNativeBorderColors(); nativeFontKeys.clear(); @@ -2394,7 +2436,7 @@ void setThemePropsImpl(Hashtable themeProps) { themeGeneration++; imageCache.clear(); if (themelisteners != null) { - themelisteners.fireActionEvent(new ActionEvent(themeProps, ActionEvent.Type.Theme)); + themelisteners.fireActionEvent(new ActionEvent(source, ActionEvent.Type.Theme)); } buildTheme(themeProps); breakTitleAreaToolbarDeriveCycle(); @@ -2444,7 +2486,7 @@ private void breakTitleAreaToolbarDeriveCycle() { } } - private void buildTheme(Hashtable themeProps) { + private void buildTheme(HashMap themeProps) { buildThemeDepth++; try { buildThemeImpl(themeProps); @@ -2453,7 +2495,7 @@ private void buildTheme(Hashtable themeProps) { } } - private void buildThemeImpl(Hashtable themeProps) { + private void buildThemeImpl(HashMap themeProps) { // A new theme may change constants that the platform consults while deriving // fonts (e.g. a native theme's text letter spacing). Flush the derived-font // cache so those fonts are rebuilt against the incoming constants rather than @@ -2472,16 +2514,14 @@ private void buildThemeImpl(Hashtable themeProps) { if (!refreshingTheme && !"true".equals(themeProps.get("@nativeThemeDefaultsBool"))) { dropSupersededBindings(themeProps); } - Enumeration e = themeProps.keys(); - while (e.hasMoreElements()) { - String key = (String) e.nextElement(); - + for (Map.Entry e : themeProps.entrySet()) { + String key = e.getKey(); // this is a constant not a theme entry if (key.startsWith("@")) { - themeConstants.put(key.substring(1), themeProps.get(key)); + themeConstants.put(key.substring(1), e.getValue()); continue; } - this.themeProps.put(key, themeProps.get(key)); + this.themeProps.put(key, e.getValue()); } String overlayThemes = (String) themeProps.get("@OverlayThemes"); @@ -2496,7 +2536,7 @@ private void buildThemeImpl(Hashtable themeProps) { Resources res = Resources.openLayered("/" + th); boolean a = accessible; accessible = true; - addThemeProps(res.getTheme(res.getThemeResourceNames()[0])); + addThemeMap(res.getThemeMap(res.getThemeResourceNames()[0])); accessible = a; } catch (Exception ex) { System.err.println("Failed to load overlay theme file specified by @overlayThemes theme constant: " + th); @@ -2586,6 +2626,10 @@ private void applyThemeBindings() { return; } final String prefix = "cn1-bind:"; + // One normalized copy per override: every key bound to `--accent-color` + // shares the String it resolves to, where normalizing per key stored 230 + // equal copies of the same few colors in the theme. + HashMap normalized = null; for (Map.Entry entry : themeConstants.entrySet()) { String constantKey = entry.getKey(); if (constantKey == null || !constantKey.startsWith(prefix)) { @@ -2616,9 +2660,17 @@ private void applyThemeBindings() { } String overrideValue = (String) override; if (themeKey.endsWith("Color")) { - overrideValue = normalizeBoundColorValue(overrideValue); + if (normalized == null) { + normalized = new HashMap(); + } + String raw = overrideValue; + overrideValue = normalized.get(raw); if (overrideValue == null) { - continue; + overrideValue = normalizeBoundColorValue(raw); + if (overrideValue == null) { + continue; + } + normalized.put(raw, overrideValue); } } themeProps.put(themeKey, overrideValue); @@ -2682,14 +2734,25 @@ private static String normalizeBoundColorValue(String raw) { if (value.length() != 6) { return null; } + boolean upper = false; for (int i = 0; i < 6; i++) { char c = value.charAt(i); boolean hex = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); if (!hex) { return null; } + upper |= c >= 'A' && c <= 'F'; + } + if (!upper) { + return value; + } + // Hex digits, folded by hand: toLowerCase() is locale sensitive. + char[] c = new char[6]; + for (int i = 0; i < 6; i++) { + char x = value.charAt(i); + c[i] = x >= 'A' && x <= 'F' ? (char) (x + ('a' - 'A')) : x; } - return value.toLowerCase(); + return new String(c); } private Map parseCache() { diff --git a/CodenameOne/src/com/codename1/ui/util/Resources.java b/CodenameOne/src/com/codename1/ui/util/Resources.java index 91075fa8a88..19aa2708eac 100644 --- a/CodenameOne/src/com/codename1/ui/util/Resources.java +++ b/CodenameOne/src/com/codename1/ui/util/Resources.java @@ -519,7 +519,7 @@ private void openFileImpl(InputStream input) throws IOException { continue; case MAGIC_THEME: case MAGIC_THEME_LEGACY: - setResource(id, MAGIC_THEME, loadTheme(id, magic == MAGIC_THEME)); + setResource(id, MAGIC_THEME, loadThemeMap(id, magic == MAGIC_THEME)); continue; case MAGIC_IMAGE: Image img = createImage(); @@ -1058,51 +1058,121 @@ public Font getFont(String id) { /// /// cached theme instance public Hashtable getTheme(String id) { - Hashtable h = (Hashtable) resources.get(id); + Object stored = resources.get(id); + if (stored instanceof HashMap) { + // Callers of this method edit the table in place (the CSS compiler, the + // resource editor), so the table handed out has to be the stored one. + HashMap map = getThemeMap(id); + Hashtable h = new Hashtable(map.size() * 2); + for (Map.Entry e : map.entrySet()) { + Object value = e.getValue(); + if (value != null) { + h.put(e.getKey(), value); + } + } + resources.put(id, h); + return h; + } + Hashtable h = (Hashtable) stored; // theme can be null in valid use cases such as the resource editor if (h != null && h.containsKey("uninitialized")) { Enumeration e = h.keys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); - if (key.endsWith("font") || (key.endsWith("Image") && !key.endsWith("scaledImage"))) { - Object value = h.get(key); - if (value == null) { - throw new IllegalArgumentException("Couldn't find resource: " + key); - } + Object value = resolveThemeEntry(key, h.get(key)); + if (value != null) { + h.put(key, value); + } + } + h.remove("uninitialized"); + } + return h; + } + + /// Returns the theme resource from the file as the map + /// [com.codename1.ui.plaf.UIManager#setThemeMap(HashMap)] takes. Prefer this + /// to [#getTheme(String)]: the theme is loaded as this map, so nothing is + /// copied, and its loops compile to direct table walks with no iterator and + /// no locking. + /// + /// #### Parameters + /// + /// - `id`: name of the theme resource + /// + /// #### Returns + /// + /// cached theme instance + public HashMap getThemeMap(String id) { + Object stored = resources.get(id); + if (stored instanceof Hashtable) { + // Set as a table (MutableResource, the resource editor) or handed out + // by getTheme: resolve it there, then keep the map from now on. + Hashtable h = getTheme(id); + HashMap map = new HashMap(h.size() * 2); + Enumeration e = h.keys(); + while (e.hasMoreElements()) { + Object key = e.nextElement(); + if (key instanceof String) { + map.put((String) key, h.get(key)); + } + } + resources.put(id, map); + return map; + } + if (!(stored instanceof HashMap)) { + return null; + } + HashMap map = (HashMap) stored; + if (map.containsKey("uninitialized")) { + for (Map.Entry e : map.entrySet()) { + Object value = resolveThemeEntry(e.getKey(), e.getValue()); + if (value != null) { + e.setValue(value); + } + } + map.remove("uninitialized"); + } + return map; + } - // the resource was not already loaded when we loaded the theme - // it must be loaded now so we can resolve the temporary name - if (value instanceof String) { - Object o; + /// The loaded form of a theme entry that still names a resource loaded after + /// the theme, or null when the entry needs nothing. + private Object resolveThemeEntry(String key, Object value) { + Object result = null; + if (key.endsWith("font") || (key.endsWith("Image") && !key.endsWith("scaledImage"))) { + if (value == null) { + throw new IllegalArgumentException("Couldn't find resource: " + key); + } - // we need to trigger multi image logic in the resource editor - if (key.endsWith("Image")) { - o = getImage((String) value); - } else { - o = resources.get(value); - } - if (o == null) { - throw new IllegalArgumentException("Theme entry for " + key + " could not be found: " + value); - } - h.put(key, o); - } + // the resource was not already loaded when we loaded the theme + // it must be loaded now so we can resolve the temporary name + if (value instanceof String) { + Object o; + + // we need to trigger multi image logic in the resource editor + if (key.endsWith("Image")) { + o = getImage((String) value); + } else { + o = resources.get(value); } - // if this is a border we might need to do additional work for older versions - // of Codename One and for the case of an image border where the images might not have - // been loaded yet when the border was created - if (key.endsWith("order")) { - Border b = confirmBorder(h, key); - h.put(key, b); + if (o == null) { + throw new IllegalArgumentException("Theme entry for " + key + " could not be found: " + value); } + result = o; + value = o; } - h.remove("uninitialized"); } - return h; + // if this is a border we might need to do additional work for older versions + // of Codename One and for the case of an image border where the images might not have + // been loaded yet when the border was created + if (key.endsWith("order")) { + result = confirmBorder(value); + } + return result; } - private Border confirmBorder(Hashtable h, String key) { - Object val = h.get(key); + private Border confirmBorder(Object val) { if (val == null) { return null; } @@ -1556,7 +1626,21 @@ private com.codename1.ui.Gradient readGradient(DataInputStream input) throws IOE } Hashtable loadTheme(String id, boolean newerVersion) throws IOException { - Hashtable theme = new Hashtable(); + HashMap map = loadThemeMap(id, newerVersion); + Hashtable theme = new Hashtable(map.size() * 2); + for (Map.Entry e : map.entrySet()) { + Object value = e.getValue(); + if (value != null) { + theme.put(e.getKey(), value); + } + } + return theme; + } + + /// Reads a theme as the map [#getThemeMap(String)] hands out; what a resource + /// file load stores. [#loadTheme(String, boolean)] is the same table copied. + HashMap loadThemeMap(String id, boolean newerVersion) throws IOException { + HashMap theme = new HashMap(); String densityStr = Display.getInstance().getDensityStr(); String platformName = Display.getInstance().getPlatformName(); if ("HTML5".equals(platformName)) { @@ -1572,14 +1656,15 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { // marks the theme as uninitialized so we can finish "wiring" cached resources theme.put("uninitialized", Boolean.TRUE); int size = input.readShort(); + ThemeStrings values = new ThemeStrings(size); List fontKeys = new ArrayList(); Map fontScaleRules = null; Map mediaRules = new HashMap(); int defaultFontSizeSetPriority = 0; for (int iter = 0; iter < size; iter++) { - String key = input.readUTF(); + String key = values.readUTF(input); if (key.startsWith("@")) { - theme.put(key, input.readUTF()); + theme.put(key, values.readUTF(input)); if (enableMediaQueries) { if (key.endsWith("-font-scale")) { if (fontScaleRules == null) { @@ -1648,7 +1733,10 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { } } - if (mediaMatch) { + // A key with no media prefix is its own translation, and a rule for it + // never wins over one that matched a prefix. A rule per plain key was + // 5,000 objects and map entries of garbage per theme load. + if (mediaMatch && matchCount > 0) { MediaRule rule = mediaRules.get(subkey); boolean replace = rule == null; if (!replace && rule.matchCount <= matchCount) { @@ -1674,7 +1762,7 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { // if this is a simple numeric value if (key.endsWith("Color")) { - theme.put(key, Integer.toHexString(input.readInt())); + theme.put(key, values.hex(input.readInt())); continue; } @@ -1685,7 +1773,7 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { // if this is a short numeric value for transparency if (key.endsWith("ransparency")) { - theme.put(key, "" + (input.readByte() & 0xff)); + theme.put(key, values.decimal(input.readByte() & 0xff)); continue; } @@ -1694,7 +1782,7 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { continue; } if (key.endsWith("opacity")) { - theme.put(key, "" + (input.readInt() & 0xff)); + theme.put(key, values.decimal(input.readInt() & 0xff)); continue; } @@ -1730,13 +1818,13 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { float p2 = input.readFloat(); float p3 = input.readFloat(); float p4 = input.readFloat(); - theme.put(key, "" + p1 + "," + p2 + "," + p3 + "," + p4); + theme.put(key, values.floats(p1, p2, p3, p4)); } else { int p1 = input.readByte() & 0xff; int p2 = input.readByte() & 0xff; int p3 = input.readByte() & 0xff; int p4 = input.readByte() & 0xff; - theme.put(key, "" + p1 + "," + p2 + "," + p3 + "," + p4); + theme.put(key, values.ints(p1, p2, p3, p4)); } continue; } @@ -1765,7 +1853,7 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { // is this a new font? if (input.readBoolean()) { - String fontId = input.readUTF(); + String fontId = values.readUTF(input); f = (Font) resources.get(fontId); // if the font is not yet loaded @@ -1778,8 +1866,8 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { if (minorVersion > 4) { boolean hasTTF = input.readBoolean(); if (hasTTF) { - String fileName = input.readUTF(); - String fontName = input.readUTF(); + String fileName = values.readUTF(input); + String fontName = values.readUTF(input); int sizeSetting = input.readInt(); float fontSize = input.readFloat(); if (isTrueTypeFileSupported()) { @@ -1810,7 +1898,7 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { // Tiled Both Image case MAGIC_INDEXED_IMAGE_LEGACY: // the image name coupled with the type - theme.put(key + Style.BG_IMAGE, input.readUTF()); + theme.put(key + Style.BG_IMAGE, values.readUTF(input)); break; // Aligned Image @@ -1820,7 +1908,7 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { // Tiled Horizontally Image case 0xF3: // the image name coupled with the type and with alignment information - String imageName = input.readUTF(); + String imageName = values.readUTF(input); theme.put(key + Style.BG_IMAGE, imageName); byte align = input.readByte(); theme.put(key + Style.BACKGROUND_ALIGNMENT, Byte.valueOf(align)); @@ -1858,13 +1946,13 @@ Hashtable loadTheme(String id, boolean newerVersion) throws IOException { // if this is a background image bgImage if (key.endsWith("derive")) { - theme.put(key, input.readUTF()); + theme.put(key, values.readUTF(input)); continue; } // if this is a background image bgImage if (key.endsWith("bgImage")) { - String imageId = input.readUTF(); + String imageId = values.readUTF(input); Image i = getImage(imageId); // if the font is not yet loaded diff --git a/CodenameOne/src/com/codename1/ui/util/ThemeStrings.java b/CodenameOne/src/com/codename1/ui/util/ThemeStrings.java new file mode 100644 index 00000000000..123430dcf42 --- /dev/null +++ b/CodenameOne/src/com/codename1/ui/util/ThemeStrings.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + +package com.codename1.ui.util; + +import java.io.DataInputStream; +import java.io.IOException; + +/// The strings of one theme load, each stored once. +/// +/// A theme repeats a few values thousands of times -- `0`, `ffffff`, +/// `native:MainRegular` -- and reading each with `readUTF`, `toHexString` or +/// `"" + int` made a byte array, a char array and a String per entry, almost all +/// of them garbage on arrival. The pages that garbage filled stay resident after +/// it is collected, so deduplicating the result afterwards saves nothing. This +/// decodes into reusable buffers and creates a String only for a value it has not +/// seen in this load; a repeated value allocates nothing. +final class ThemeStrings { + private byte[] bytes = new byte[64]; + private char[] chars = new char[64]; + private String[] table; + private int size; + + /// `entries` is the entry count the theme declares. Every key is a distinct + /// string and the values add a few hundred more, so a table sized for that up + /// front never has to grow: growing a 5,000 entry theme from a small table + /// discarded four tables on the way. + ThemeStrings(int entries) { + int want = Math.max(entries, 0) * 2 + 1024; + int len = 1024; + while (len < want && len < (1 << 20)) { + len <<= 1; + } + table = new String[len]; + } + + /// Reads a string written by `DataOutputStream.writeUTF` (modified UTF-8). + String readUTF(DataInputStream in) throws IOException { + int len = in.readUnsignedShort(); + if (bytes.length < len) { + bytes = new byte[len]; + } + in.readFully(bytes, 0, len); + if (chars.length < len) { + chars = new char[len]; + } + byte[] b = bytes; + char[] c = chars; + int n = 0; + int i = 0; + while (i < len) { + int x = b[i] & 0xff; + if (x < 0x80) { + c[n++] = (char) x; + i++; + } else if ((x & 0xe0) == 0xc0) { + if (i + 1 >= len || (b[i + 1] & 0xc0) != 0x80) { + throw new IOException("Malformed UTF-8 in theme"); + } + c[n++] = (char) (((x & 0x1f) << 6) | (b[i + 1] & 0x3f)); + i += 2; + } else if ((x & 0xf0) == 0xe0) { + if (i + 2 >= len || (b[i + 1] & 0xc0) != 0x80 || (b[i + 2] & 0xc0) != 0x80) { + throw new IOException("Malformed UTF-8 in theme"); + } + c[n++] = (char) (((x & 0x0f) << 12) | ((b[i + 1] & 0x3f) << 6) | (b[i + 2] & 0x3f)); + i += 3; + } else { + throw new IOException("Malformed UTF-8 in theme"); + } + } + return intern(n); + } + + /// `Integer.toHexString(v)`. + String hex(int v) { + char[] c = chars; + int n = 0; + int shift = 28; + while (shift > 0 && ((v >>> shift) & 0xf) == 0) { + shift -= 4; + } + for (; shift >= 0; shift -= 4) { + int d = (v >>> shift) & 0xf; + c[n++] = (char) (d < 10 ? '0' + d : 'a' + d - 10); + } + return intern(n); + } + + /// `"" + v` for a value from 0 to 255, which is all a theme stores this way. + String decimal(int v) { + char[] c = chars; + int n = 0; + if (v >= 100) { + c[n++] = (char) ('0' + v / 100); + } + if (v >= 10) { + c[n++] = (char) ('0' + v / 10 % 10); + } + c[n++] = (char) ('0' + v % 10); + return intern(n); + } + + /// `"" + a + "," + b + "," + c + "," + d`, the form padding and margin are + /// stored in. Each `+` on a float made a String of its own. + String floats(float a, float b, float c, float d) { + ensureChars(64); + int n = appendFloat(a, 0); + chars[n++] = ','; + n = appendFloat(b, n); + chars[n++] = ','; + n = appendFloat(c, n); + chars[n++] = ','; + n = appendFloat(d, n); + return intern(n); + } + + /// `"" + a + "," + b + "," + c + "," + d` for the integer form older themes use. + String ints(int a, int b, int c, int d) { + ensureChars(64); + int n = appendInt(a, 0); + chars[n++] = ','; + n = appendInt(b, n); + chars[n++] = ','; + n = appendInt(c, n); + chars[n++] = ','; + n = appendInt(d, n); + return intern(n); + } + + /// Appends `Float.toString(f)`. A theme's values have at most one decimal + /// (2.0, 0.5, 48.9); those are written digit by digit, which is what + /// `Float.toString` prints for them, and anything else is handed to it. + /// The digits are the shortest form that reads back as `f`; a VM whose + /// `Float.toString` prints more digits for such a value produced a + /// longer string before, never a different float once the theme parses it. + private int appendFloat(float f, int n) { + float tenths = f * 10f; + int t = (int) tenths; + if (t == tenths && t > -10000000 && t < 10000000 && (float) (t / 10.0) == f && !(t == 0 && 1 / f < 0)) { + if (t < 0) { + chars[n++] = '-'; + t = -t; + } + n = appendInt(t / 10, n); + chars[n++] = '.'; + chars[n++] = (char) ('0' + t % 10); + return n; + } + String s = Float.toString(f); + int len = s.length(); + ensureChars(n + len + 48); + s.getChars(0, len, chars, n); + return n + len; + } + + private int appendInt(int v, int n) { + if (v < 0) { + String s = String.valueOf(v); + int len = s.length(); + s.getChars(0, len, chars, n); + return n + len; + } + int start = n; + do { + chars[n++] = (char) ('0' + v % 10); + v /= 10; + } while (v != 0); + for (int i = start, j = n - 1; i < j; i++, j--) { + char x = chars[i]; + chars[i] = chars[j]; + chars[j] = x; + } + return n; + } + + private void ensureChars(int n) { + if (chars.length < n) { + char[] c = new char[n]; + System.arraycopy(chars, 0, c, 0, chars.length); + chars = c; + } + } + + /// The shared copy of a string built elsewhere. + String share(String s) { + int n = s.length(); + if (chars.length < n) { + chars = new char[n]; + } + s.getChars(0, n, chars, 0); + return intern(n); + } + + private String intern(int n) { + char[] c = chars; + int h = 0; + for (int i = 0; i < n; i++) { + h = 31 * h + c[i]; + } + String[] t = table; + int mask = t.length - 1; + int i = (h ^ (h >>> 16)) & mask; + while (true) { + String s = t[i]; + if (s == null) { + break; + } + if (s.length() == n && same(s, c, n)) { + return s; + } + i = (i + 1) & mask; + } + String s = new String(c, 0, n); + t[i] = s; + if (++size * 2 > t.length) { + grow(); + } + return s; + } + + private static boolean same(String s, char[] c, int n) { + for (int i = 0; i < n; i++) { + if (s.charAt(i) != c[i]) { + return false; + } + } + return true; + } + + private void grow() { + String[] old = table; + String[] t = new String[old.length * 2]; + int mask = t.length - 1; + for (String s : old) { + if (s != null) { + int h = s.hashCode(); + int i = (h ^ (h >>> 16)) & mask; + while (t[i] != null) { + i = (i + 1) & mask; + } + t[i] = s; + } + } + table = t; + } +} diff --git a/CodenameOneDesigner/src/com/codename1/designer/PreviewInSimulator.java b/CodenameOneDesigner/src/com/codename1/designer/PreviewInSimulator.java index 5166642ed75..cd6e593d8f4 100644 --- a/CodenameOneDesigner/src/com/codename1/designer/PreviewInSimulator.java +++ b/CodenameOneDesigner/src/com/codename1/designer/PreviewInSimulator.java @@ -144,7 +144,7 @@ public void run() { com.codename1.ui.Display.getInstance().installNativeTheme(); } } else { - com.codename1.ui.plaf.UIManager.getInstance().setThemeProps(res.getTheme(theme)); + com.codename1.ui.plaf.UIManager.getInstance().setThemeMap(res.getThemeMap(theme)); } com.codename1.ui.util.UIBuilder.registerCustomComponent("Table", com.codename1.ui.table.Table.class); com.codename1.ui.util.UIBuilder.registerCustomComponent("MediaPlayer", com.codename1.components.MediaPlayer.class); diff --git a/CodenameOneDesigner/src/com/codename1/designer/ResourceEditorApp.java b/CodenameOneDesigner/src/com/codename1/designer/ResourceEditorApp.java index 482bcfb99a9..6da49f378ec 100644 --- a/CodenameOneDesigner/src/com/codename1/designer/ResourceEditorApp.java +++ b/CodenameOneDesigner/src/com/codename1/designer/ResourceEditorApp.java @@ -833,7 +833,7 @@ private static void migrateGuiBuilder(File projectDir, EditableResources res, St stateMachineBaseSource.append(" String[] themes = res.getThemeResourceNames();\n"); stateMachineBaseSource.append(" Resources.setGlobalResources(res);\n"); stateMachineBaseSource.append(" if(themes != null && themes.length > 0) {\n"); - stateMachineBaseSource.append(" UIManager.getInstance().setThemeProps(res.getTheme(themes[0]));\n"); + stateMachineBaseSource.append(" UIManager.getInstance().setThemeMap(res.getThemeMap(themes[0]));\n"); stateMachineBaseSource.append(" }\n"); stateMachineBaseSource.append(" }\n\n\n"); stateMachineBaseSource.append(" public StateMachineBase() {\n instance = this;\n"); diff --git a/CodenameOneDesigner/src/com/codename1/designer/ResourceEditorView.java b/CodenameOneDesigner/src/com/codename1/designer/ResourceEditorView.java index 954b9fc0bcf..be8effb58dc 100644 --- a/CodenameOneDesigner/src/com/codename1/designer/ResourceEditorView.java +++ b/CodenameOneDesigner/src/com/codename1/designer/ResourceEditorView.java @@ -2491,7 +2491,7 @@ protected com.codename1.ui.Component createComponentInstance(String componentTyp w.write(" protected void initTheme(Resources res) {\n"); w.write(" String[] themes = res.getThemeResourceNames();\n"); w.write(" if(themes != null && themes.length > 0) {\n"); - w.write(" UIManager.getInstance().setThemeProps(res.getTheme(themes[0]));\n"); + w.write(" UIManager.getInstance().setThemeMap(res.getThemeMap(themes[0]));\n"); w.write(" }\n"); w.write(" }\n\n"); diff --git a/Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java b/Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java index fa81cfe3ef6..21f61f9c5fc 100644 --- a/Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java +++ b/Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java @@ -6089,9 +6089,9 @@ public void installNativeTheme() { is = getResourceAsStream(getClass(), "/android_holo_light.res"); } Resources r = Resources.open(is); - Hashtable h = r.getTheme(r.getThemeResourceNames()[0]); + HashMap h = r.getThemeMap(r.getThemeResourceNames()[0]); h.put("@commandBehavior", "Native"); - UIManager.getInstance().setThemeProps(h); + UIManager.getInstance().setThemeMap(h); is.close(); Display.getInstance().setCommandBehavior(Display.COMMAND_BEHAVIOR_NATIVE); } catch (IOException ex) { diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/CSSWatcher.java b/Ports/JavaSE/src/com/codename1/impl/javase/CSSWatcher.java index 9e21723f662..fc195c13955 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/CSSWatcher.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/CSSWatcher.java @@ -672,7 +672,7 @@ public void run() { try { System.out.println("CSS File "+compilerArgs.input+" has been updated. Reloading styles from "+fDestFile); Resources res = Resources.open(new FileInputStream(fDestFile)); - UIManager.getInstance().addThemeProps(res.getTheme(res.getThemeResourceNames()[0])); + UIManager.getInstance().addThemeMap(res.getThemeMap(res.getThemeResourceNames()[0])); Form f = CN.getCurrentForm(); if (f != null) { f.refreshTheme(); diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/ComponentTreeInspector.java b/Ports/JavaSE/src/com/codename1/impl/javase/ComponentTreeInspector.java index a411dd92c40..d5442b336c3 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/ComponentTreeInspector.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/ComponentTreeInspector.java @@ -742,7 +742,7 @@ public void run() { public void run() { try { Resources res = Resources.open(new FileInputStream(themeFile)); - UIManager.getInstance().addThemeProps(res.getTheme(themeName)); + UIManager.getInstance().addThemeMap(res.getThemeMap(themeName)); Display.getInstance().getCurrent().refreshTheme(); Display.getInstance().getCurrent().revalidate(); } catch(Exception err) { diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java index daab9161991..e694252b652 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java @@ -3435,7 +3435,7 @@ public void installNativeTheme() { if (nativeTheme != null) { try { Resources r = Resources.open(nativeTheme); - Hashtable h = r.getTheme(r.getThemeResourceNames()[0]); + HashMap h = r.getThemeMap(r.getThemeResourceNames()[0]); Preferences pref = Preferences.userNodeForPackage(JavaSEPort.class); boolean desktopSkin = pref.getBoolean("desktopSkin", false); if(desktopSkin) { @@ -3444,15 +3444,15 @@ public void installNativeTheme() { h.remove("@paintsTitleBarBool"); } injectDesktopThemeConstants(h); - UIManager.getInstance().setThemeProps(h); + UIManager.getInstance().setThemeMap(h); } catch (IOException ex) { ex.printStackTrace(); } } else { if (nativeThemeRes != null) { - Hashtable h = nativeThemeRes.getTheme(nativeThemeRes.getThemeResourceNames()[0]); + HashMap h = nativeThemeRes.getThemeMap(nativeThemeRes.getThemeResourceNames()[0]); injectDesktopThemeConstants(h); - UIManager.getInstance().setThemeProps(h); + UIManager.getInstance().setThemeMap(h); } } } @@ -3463,7 +3463,7 @@ public void installNativeTheme() { /// them on mobile because this method is a no-op there. Values come from the static config /// (set by the generated desktop Stub) with a {@code codename1.arg.desktop.*} system-property /// fallback so build hints work too. - private void injectDesktopThemeConstants(Hashtable h) { + private void injectDesktopThemeConstants(HashMap h) { if (h == null || !isDesktop()) { return; } diff --git a/Ports/JavaSE/src/com/codename1/testing/junit/CodenameOneExtension.java b/Ports/JavaSE/src/com/codename1/testing/junit/CodenameOneExtension.java index dc231c7c132..a147c3d8584 100644 --- a/Ports/JavaSE/src/com/codename1/testing/junit/CodenameOneExtension.java +++ b/Ports/JavaSE/src/com/codename1/testing/junit/CodenameOneExtension.java @@ -39,7 +39,7 @@ import java.awt.GraphicsEnvironment; import java.lang.reflect.Method; -import java.util.Hashtable; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicReference; /** @@ -336,8 +336,8 @@ private static void installTheme(String resourcePath, ExtensionContext context) throw new IllegalStateException( "Theme resource " + resourcePath + " contains no themes"); } - Hashtable themeProps = r.getTheme(names[0]); - UIManager.getInstance().setThemeProps(themeProps); + HashMap themeProps = r.getThemeMap(names[0]); + UIManager.getInstance().setThemeMap(themeProps); context.getStore(FONT_STATE).put("restore", restore); } catch (java.io.IOException | RuntimeException | Error e) { restore.run(); diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java index 78cceabd0b3..697844112b5 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java @@ -5464,7 +5464,7 @@ public void installNativeTheme(){ String fallback = isAndroid_() ? "/android_holo_light.res" : "/iOS7Theme.res"; r = Resources.open(fallback); } - Hashtable tp = r.getTheme(r.getThemeResourceNames()[0]); + HashMap tp = r.getThemeMap(r.getThemeResourceNames()[0]); // The browser has no OS status bar / notch, so the app must not // reserve a status-bar strip at the top of the Form the way iOS @@ -5491,7 +5491,7 @@ public void installNativeTheme(){ // native menu bar at that moment -- which it does until the HTML chrome exists. Forms // without a Toolbar then never published their MenuBar to it. installDesktopChromeIfRequested(); - UIManager.getInstance().setThemeProps(tp); + UIManager.getInstance().setThemeMap(tp); return; } catch (IOException ex){ Log.e(ex); diff --git a/Ports/LinuxPort/src/com/codename1/impl/linux/LinuxImplementation.java b/Ports/LinuxPort/src/com/codename1/impl/linux/LinuxImplementation.java index 344463ff8b7..da589a11b10 100644 --- a/Ports/LinuxPort/src/com/codename1/impl/linux/LinuxImplementation.java +++ b/Ports/LinuxPort/src/com/codename1/impl/linux/LinuxImplementation.java @@ -469,7 +469,7 @@ public void installNativeTheme() { com.codename1.ui.util.Resources r = com.codename1.ui.util.Resources.open(in); String[] names = r.getThemeResourceNames(); if (names != null && names.length > 0) { - com.codename1.ui.plaf.UIManager.getInstance().setThemeProps(r.getTheme(names[0])); + com.codename1.ui.plaf.UIManager.getInstance().setThemeMap(r.getThemeMap(names[0])); } } catch (Throwable t) { // A bad/absent theme must not stop the app from starting. diff --git a/Ports/WindowsPort/src/com/codename1/impl/windows/WindowsImplementation.java b/Ports/WindowsPort/src/com/codename1/impl/windows/WindowsImplementation.java index 372247d2b76..20230d0598d 100644 --- a/Ports/WindowsPort/src/com/codename1/impl/windows/WindowsImplementation.java +++ b/Ports/WindowsPort/src/com/codename1/impl/windows/WindowsImplementation.java @@ -374,7 +374,7 @@ public void installNativeTheme() { com.codename1.ui.util.Resources r = com.codename1.ui.util.Resources.open(in); String[] names = r.getThemeResourceNames(); if (names != null && names.length > 0) { - com.codename1.ui.plaf.UIManager.getInstance().setThemeProps(r.getTheme(names[0])); + com.codename1.ui.plaf.UIManager.getInstance().setThemeMap(r.getThemeMap(names[0])); } } catch (Throwable t) { // A bad/absent theme must not stop the app from starting. diff --git a/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java b/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java index f7680d13aac..e70b9240851 100644 --- a/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java +++ b/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java @@ -3157,9 +3157,9 @@ public void installNativeTheme() { InputStream sub = getResourceAsStream("/" + subclassTheme + ".res"); if (sub != null) { r = Resources.open(sub); - Hashtable tp = r.getTheme(r.getThemeResourceNames()[0]); + HashMap tp = r.getThemeMap(r.getThemeResourceNames()[0]); injectDesktopThemeConstants(tp); - UIManager.getInstance().setThemeProps(tp); + UIManager.getInstance().setThemeMap(tp); return; } // Not in the bundle (a framework build that has not generated it yet): @@ -3192,9 +3192,9 @@ public void installNativeTheme() { } if (in != null) { r = Resources.open(in); - Hashtable tp = r.getTheme(r.getThemeResourceNames()[0]); + HashMap tp = r.getThemeMap(r.getThemeResourceNames()[0]); injectDesktopThemeConstants(tp); - UIManager.getInstance().setThemeProps(tp); + UIManager.getInstance().setThemeMap(tp); return; } // Modern theme isn't in the jar (e.g. framework build hasn't @@ -3202,19 +3202,19 @@ public void installNativeTheme() { } if(mode.equals("ios7") || mode.equals("flat") || mode.equals("auto") || mode.equals("modern") || mode.equals("liquid")) { r = Resources.open("/iOS7Theme.res"); - Hashtable tp = r.getTheme(r.getThemeResourceNames()[0]); + HashMap tp = r.getThemeMap(r.getThemeResourceNames()[0]); if(!nativeInstance.isIOS7()) { tp.put("TitleArea.padding", "0,0,0,0"); } injectDesktopThemeConstants(tp); - UIManager.getInstance().setThemeProps(tp); + UIManager.getInstance().setThemeMap(tp); return; } // "legacy" / "iphone" / anything else: pre-flat iPhone theme. r = Resources.open("/iPhoneTheme.res"); - Hashtable tp = r.getTheme(r.getThemeResourceNames()[0]); + HashMap tp = r.getThemeMap(r.getThemeResourceNames()[0]); injectDesktopThemeConstants(tp); - UIManager.getInstance().setThemeProps(tp); + UIManager.getInstance().setThemeMap(tp); } catch (IOException ex) { ex.printStackTrace(); } @@ -3226,7 +3226,7 @@ public void installNativeTheme() { * native menu bar). These theme constants are injected only on the desktop so iOS * phones/tablets are unaffected. Mirrors JavaSEPort.injectDesktopThemeConstants. */ - private void injectDesktopThemeConstants(Hashtable tp) { + private void injectDesktopThemeConstants(HashMap tp) { if (tp == null || !isDesktop()) { return; } @@ -3264,7 +3264,7 @@ private void injectDesktopThemeConstants(Hashtable tp) { * system accent is used rather than a colour invented here, and the guard * leaves any theme that does define the UIID alone. */ - private void injectListFocusStyle(Hashtable tp) { + private void injectListFocusStyle(HashMap tp) { // Any entry at all under the UIID means the theme has an opinion; leave it. for (Object key : tp.keySet()) { if (key instanceof String && ((String) key).indexOf("ListRendererFocus") > -1) { diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc new file mode 100644 index 00000000000..87f6dbd943d --- /dev/null +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -0,0 +1,237 @@ +[[kmp-interop]] +== Running Kotlin Multiplatform and Compose applications + +A Kotlin Multiplatform (KMP) module whose user interface is written with +Compose Multiplatform builds as a Codename One application. The shared +`commonMain` code is compiled unchanged, the composables render as Codename +One components, and the result ships to every Codename One target: iOS, +Android, the web, macOS, Windows and Linux (as native binaries with no JVM), +watchOS and tvOS. + +=== Motivation + +Compose Multiplatform draws its whole user interface with Skia, and on iOS it +runs on Kotlin/Native. Both come with the application: a large native +runtime, a drawing engine that doesn't use the platform's text input, +accessibility or look, and a second toolchain to keep in step with the JVM +one. + +Codename One takes the JVM half of a KMP project instead. kotlinc already +compiles `commonMain` to ordinary class files for Android and the desktop. +Those classes, together with the real Compose runtime and the real +`kotlinx.coroutines`, are exactly what Codename One's translator reads. Only +the UI layer -- `androidx.compose.ui`, `foundation` and `material3` -- is +replaced, by one written over Codename One components and `Graphics`. + +The same application, built from the same `commonMain` both ways and measured +on the same iPhone simulator: + +image::img/kmp-bench-results.svg["Executable, bundle, memory and start-up of the same Compose application built by Codename One and by Compose Multiplatform",scaledwidth=95%] + +The executable is 28% smaller, although the Codename One one still carries +the whole iOS port and the full Material icon font. Memory and start-up are +level: an empty UIKit application takes 640-750 ms to its first frame on the +same simulator, so neither side has much start-up left to lose there. The +benchmark, its method and how to run it are in `scripts/kmp-bench` in the +Codename One repository. + +image::img/kmp-keynote-schedule.png["The Keynote schedule screen rendered by Compose Multiplatform (left) and by Codename One (right)",scaledwidth=70%] + +Beyond size, the composables become real components, so a Compose screen +uses the platform's own text input and accessibility, can sit inside a +Codename One form next to any other component, and reaches every Codename One +API directly -- push notifications, in-app purchase, the native maps, the +backend -- with no `expect`/`actual` plumbing per platform. + +=== Quick start + +Create a Codename One application (for example at +https://start.codenameone.com) and import the KMP project into it: + +[source,bash] +---- +mvn cn1:import-kmp-project -Dcn1.kmp.import=/path/to/MyKmpApp +---- + +The goal copies the module's `commonMain` and its JVM source set (`jvmMain`, +else `desktopMain`) into `common/src/main/kmp`, and rewrites the +application's main class so that it shows the module's root composable. Two +options cover projects it can't decide alone: + +`-Dcn1.kmp.module=composeApp`:: The module to import, when more than one has a +`src/commonMain`. +`-Dcn1.kmp.entry=com.example.App`:: The fully qualified root composable, when +`commonMain` declares no single `@Composable fun App()`. + +The Android and iOS source sets aren't copied. They call those platforms +directly, and Codename One supplies both. A JVM file whose `main` opens a +desktop window (`application { Window { App() } }`) is left out too: the +generated main class starts the application instead. + +Then build and run as usual: + +[source,bash] +---- +mvn cn1:run # the simulator +mvn cn1:build -Dcodename1.platform=ios # an iOS build on the build server +---- + +==== Gradle + +A Gradle application needs the Compose compiler plugin applied next to the +Codename One plugin. The build stops with the line to add when it's missing: + +[source,kotlin] +---- +plugins { + id("org.jetbrains.kotlin.plugin.compose") version "2.2.10" +} +---- + +`convert-to-gradle` writes it for you. The Codename One settings plugin adds +Google's Maven repository, limited to the `androidx.*` groups, which is where +the Compose runtime is published. + +==== Placing the sources by hand + +`common/src/main/kmp/commonMain/kotlin` and `common/src/main/kmp/jvmMain/kotlin` +are ordinary Kotlin source roots. The archetype's `compose` profile turns on +whenever `common/src/main/kmp` exists: it adds `codenameone-compose-compat`, +applies the Compose compiler plugin and sets both kotlinc's `jvmTarget` and +javac's source and target to 11, because the Compose runtime's inline functions are +JVM 11 bytecode. The Compose compiler plugin's version must equal the Kotlin +version, which is why both come from one property. + +=== The entry point + +A composition is shown in a `ComposeForm`, or embedded anywhere as a +`ComposeView`, which is an ordinary Codename One `Container`: + +[source,kotlin] +---- +class MyApp : Lifecycle() { + override fun runApp() { + ComposeForm { App() }.show() + } +} + +// Or beside other components: +val form = Form("Mixed", BorderLayout()) +form.add(BorderLayout.NORTH, Label("Codename One")) +form.add(BorderLayout.CENTER, ComposeView { Schedule() }) +form.show() +---- + +Both live in `com.codename1.compose`. From Java, a `ComposeView` is created +by a small Kotlin function that returns one, since a composable lambda can +only be written in Kotlin. + +=== How the build works + +image::img/kmp-build-pipeline.svg["KMP sources compiled by kotlinc with the Compose plugin, shipped with the unmodified Compose runtime and coroutines, then translated by ParparVM",scaledwidth=95%] + +There is no transpiler. kotlinc compiles the sources for the JVM, with the +Compose compiler plugin, against `compose-compat`. The application's +classes, the unmodified Compose runtime, `kotlinx.coroutines` and +`compose-compat` are then handled like any other Codename One code: the +bytecode compliance check runs over them, and ParparVM translates them to C +for iOS, the watch, TV and the native desktop targets. Android runs the +classes on ART and the simulator on the JVM. + +`androidx/compose/` is never relocated, not even when the +xref:Android-Interop.asciidoc[Android compatibility layer] is active in the +same application: the Compose runtime the application was compiled against +is the one it runs. + +=== How a composable becomes components + +image::img/kmp-runtime-layers.svg["The application's composables over the unmodified Compose runtime, over compose-compat, over Codename One core and the port",scaledwidth=95%] + +Composition, snapshots, `remember`, `LaunchedEffect` and recomposition are +the real Compose runtime's. What it emits are `UiNode` objects, each a +Codename One `Container`: + +* **Measuring is Compose's.** A node walks its modifier chain outermost first + under Compose constraints (`Box`, `Row`/`Column` and leaf policies). The + Codename One layout pass only copies the result out, so `weight`, + `fillMaxWidth`, `wrapContentSize` and intrinsic padding behave as they do + in Compose. +* **Frames come from the Codename One animation loop.** `ComposeHost` runs + the `Recomposer` with a `BroadcastFrameClock` that `Form.registerAnimated` + ticks, so `withFrameNanos`, `animate*AsState` and `AnimatedVisibility` run + in step with every other Codename One animation, and an idle screen costs + nothing. +* **`Dispatchers.Main` is the EDT.** kotlinx.coroutines finds its main + dispatcher through a service file. There is no service loader on the + device, so the translator compiles that file into a registry at build time. +* **Lazy lists compose what's in view.** `LazyColumn` and `LazyRow` compose + each item as its own composition when it comes within a quarter of a view + of the window and dispose of it half a view past it. `scrollToItem` works + on items that were never measured. +* **The system bars are respected.** `TopAppBar`, `CenterAlignedTopAppBar` + and `NavigationBar` pad themselves by the display's safe area, read when + they're measured, so a notch or a home indicator that the native view + reports late still moves them. + +=== Supported API + +`compose-compat` covers what typical application screens use. Anything else +is a build error (see below), never a silent no-op. + +Layout:: `Box`, `Row`, `Column`, `Spacer`, `LazyColumn`, `LazyRow`, +`Arrangement`, `Alignment`, `PaddingValues`, `Canvas`. +Modifiers:: size (`size`, `width`, `height`, `fillMax*`, `wrapContent*`, +`*In`, `required*`, `defaultMinSize`, `aspectRatio`), `padding`, `offset`, +`weight`, `background`, `border`, `clip`, `alpha`, `drawBehind`, +`clickable`, `combinedClickable`, `verticalScroll`, `horizontalScroll`, +`testTag`. +Text:: `Text`, `BasicText`, `TextField`, `OutlinedTextField`, +`BasicTextField`, `TextStyle`, `FontWeight`, the Material 3 type scale. +Material 3:: `MaterialTheme` and `ColorScheme`, `Scaffold`, `Surface`, +`TopAppBar`, `CenterAlignedTopAppBar`, `NavigationBar` and +`NavigationBarItem`, `Card`, `ElevatedCard`, `OutlinedCard`, `Button`, +`ElevatedButton`, `FilledTonalButton`, `OutlinedButton`, `TextButton`, +`IconButton`, `FloatingActionButton`, `ExtendedFloatingActionButton`, +`AssistChip`, `FilterChip`, `Checkbox`, `Switch`, the progress indicators, +dividers, `Icon` with `Icons.Filled`, `Icons.Outlined` and +`Icons.AutoMirrored`. +Animation:: `AnimatedVisibility`, `animate*AsState`, +`rememberInfiniteTransition` with `animateFloat`. +Images:: `Image` with `ImageVector` and `ContentScale`. + +`LazyColumn(reverseLayout = true)` throws: it's not implemented. + +==== Unsupported API is a build error + +The bytecode compliance check runs over the compiled classes. A composable or +modifier that `compose-compat` doesn't provide is a reference to something +that doesn't exist, and the build reports it under its original name with +the source line, before anything is translated. Port such a screen by +replacing the call, or by writing the missing piece in the application with +the supported building blocks -- most custom components are a `Box` with a +`drawBehind`. + +=== Testing + +Compose screens are tested the way any Codename One screen is: a +`ComposeView` is a component, so screenshot tests and the component inspector +see it like any other. The Codename One test application +(`scripts/hellocodenameone`) carries Compose screens under +`common/src/main/kmp` -- layout and modifiers, typography and buttons, a +`Scaffold` over a `LazyColumn` of cards, and state with `LaunchedEffect` and +`AnimatedVisibility` -- and its `KmpCompat*` screenshot tests compare them on +every platform the test suite runs on. + +=== Troubleshooting + +The Compose compiler plugin reports a Kotlin version mismatch:: The plugin +and kotlinc are different versions. Take both from one property. +kotlinc or javac reject JVM 11 bytecode or inline functions:: The module is +compiling below JVM 11. The `compose` profile sets it: check that +`common/src/main/kmp` exists and that nothing in the pom overrides +`kotlin.compiler.jvmTarget`. +The Gradle build stops asking for `org.jetbrains.kotlin.plugin.compose`:: Add +the plugin line shown in the Gradle section. +`Incremental compilation failed` after changing the Kotlin version:: Kotlin's +incremental cache from the old compiler is in `target`. Build once with +`clean`. diff --git a/docs/developer-guide/developer-guide.asciidoc b/docs/developer-guide/developer-guide.asciidoc index 21a24930daf..451d7b87a5d 100644 --- a/docs/developer-guide/developer-guide.asciidoc +++ b/docs/developer-guide/developer-guide.asciidoc @@ -274,6 +274,8 @@ include::Unity-Interop.asciidoc[] include::Desktop-Interop.asciidoc[] +include::Kotlin-Multiplatform-Interop.asciidoc[] + include::Working-with-Mac-OS-X.asciidoc[] include::Working-with-Mac-Catalyst.asciidoc[] diff --git a/docs/developer-guide/img/kmp-bench-results.svg b/docs/developer-guide/img/kmp-bench-results.svg new file mode 100644 index 00000000000..39ff1e0ba35 --- /dev/null +++ b/docs/developer-guide/img/kmp-bench-results.svg @@ -0,0 +1,31 @@ + + + + Keynote on an iPhone 17 Pro simulator: the same commonMain built both ways + Release builds, five runs; a start-up bar sits mid-range. Smaller is better. Start-up of an empty UIKit app is 640-750 ms on the + same simulator, so start-up is near the floor for both; memory is level; the executable is where the gap is. + Executable, stripped (MB) + + 16.4 + + 22.9 + App bundle (MB) + + 27.3 + + 28.8 + Physical footprint (MB) + + 47-48 + + 48 + Start-up (ms, runs 2-5) + + 596-685 + + 646-794 + + Codename One + + Compose Multiplatform + diff --git a/docs/developer-guide/img/kmp-build-pipeline.svg b/docs/developer-guide/img/kmp-build-pipeline.svg new file mode 100644 index 00000000000..2362b903fb1 --- /dev/null +++ b/docs/developer-guide/img/kmp-build-pipeline.svg @@ -0,0 +1,77 @@ + + + + What the build does with Kotlin Multiplatform sources + kotlinc compiles commonMain for the JVM with the Compose compiler plugin, exactly as an Android build would. + The classes, the unmodified Compose runtime and kotlinx.coroutines are then translated like any other Codename One code. + Your module + + commonMain + shared Kotlin, @Composable UI + + jvmMain / desktopMain + minus the file that opens a window + + common/src/main/java + generated main class + cn1:import-kmp-project copies the + first two into common/src/main/kmp + Compile + + kotlinc + Compose plugin + JVM target 11 + against compose-compat + + + + Classes that ship + + your classes + + Compose runtime + unmodified jar + + kotlinx.coroutines + unmodified jar + + compose-compat + the UI layer, ours + + + + bytecode-compliance + unsupported API = build error + + + + Targets + + ParparVM: bytecode to C, Xcode / native toolchain + + iOS + + + watchOS / tvOS + + + Windows / Linux + no JVM + + + macOS + + + Android + classes run on ART + + + Simulator / desktop + classes run on the JVM + + + One Compose source, one app + Gradle or Maven, the same plugin goals + Dispatchers.Main is the EDT + no Skia, no Kotlin/Native runtime + native theme, native text input + diff --git a/docs/developer-guide/img/kmp-keynote-schedule.png b/docs/developer-guide/img/kmp-keynote-schedule.png new file mode 100644 index 0000000000000000000000000000000000000000..10660e0dce9aca3068dd7908d02bd20a6296b6d5 GIT binary patch literal 302200 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

AJC?;d@v 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 z oG;9zso4=nL8KjDdhgR2SoNv{q0k)1`j%yVXqv4!q` z+J98H7IRxPsyO)Jv1tbxY!`|G8P1SBLdjc-jsprsXic0>Wi5U1%i|?SP{mLRp2PTW zNQ-Uj+$P1< zk}K7*nGoSOM0~ss(#D{5-Q;eHv*Q-xnH%(=JDBBS^j1C8jme=Qd8o_V{y+&F7 zNZ`DxY>HX97whNi-S&OuZY~iB$~zdJz|C^G)a>*sOMp#~H`<^B32jw5k_ROBsZ%nR zxV%gatKdp8`u!ZHv`^(Sl=yqe_e?KN4=^y$Z`>A1%uHS-TfgA(v?x`nm82;6&f0um z8+kYO#a%IoCN5URKnYMO#Z*r_va03x&*eqb*3<^$JPwBiKp5>3ZAN4Uw@>*&jSLe+ zQbI_EHSi3JMi>$+RVdMjS#>|LHb?kB{ByVw`#`-IMNMkhAl3QtE?%k9GokNJktox; zUy!|qi_j@iis+hihU|r+TqY#F;us>NVohpp_?t{=;mv2(x|hsiewZBJ--G-)9g)jp$yZ&Egov8U_x_na9LA)*h6o(9`;w?~(wRjV{|dG%*<&>>LIrj!5(#fqMo$?r)51CGcdL zD%jK7y)Q#gU{@XE8J=^d40rt8K|jW*$O)@#IjLQH$c5`IjHh^^{cdl-ONxeGPR+F7 zgv9i%9~HcR-wSfqxb1&N(n;bZG3oGV;bbS?iD8g^^XOo5Hc)SKZSBlwcIIm}Asddo z+W&nandJy;6b0ex+FJVS*RP@IAf_rV@pYW@f7NGWV*|%*Xi3rKKpX*l;BS57js|<4 zW`~=go;`{0HequjInk&1%F;Ws^VWnss`O80bg*q{Ayd>p$-4+Y^J?6cw*)cKxoXMi zr z296`334Ia$pt%2N_@!lfC2KX_7#N3#nHR}tU;-USEf>S4wtFl!INfrWu7{#!t4lt+#UVVxE(An&W&R3WND zKdVm1@sZrjU_7fFTS!e^8ptH|^;+uY-+Q9TU?)jo*82E51iWvb=}}n3(mmua?%SA$Y;=c( z<&$akTOptLzeu`#yC&LQ7{X3}7=!V*T2GYD>St+r(v!2ZkxbrZ?~*%GXe=SIm9u%% zj7TOlLy0+)Ime0HdK@1#r_-9!cZJaLA39*VJ2&${fAcaNYmfukw|8-NG$+vFcyS7$ znv??BLC*&*)a%j2dS7NVxQ;Og1zlZUoN1Qntj5Wr|JOln3H}}A88mi%+BRioZJ&B% z?`e^uL91!T^yK`U-NAMpr8=mb*8z(RCn?*XRXKmZ`{42y&mxeY&Rr~ZUBkVCBfNqi z%c-5l)&^k)%Ur+ZB80w6o|-N(%BbkTcLpm*rBT!BZQ~+Mb6-A_`-8jpo=oNDUac{^=6 zRMZ2>$2$2(q++$jZpLo3v>5kiSfh9A2|B}aKAaCx&$pGQeY6Hm99ojDC@{cO2= z!>H^J-KSLkBH29|U?36Nzb&*4H*tP5%6l78g=CBH`@W)@Ryc>_P;ey7hs_hvm^x+( z4N2SGcf>PM!8o)W0fR@1x9YX)9TR$bOUpSfD|qF&IeITnj{q;6^ykl?9HYQGvo*(I zmGD^dTGPcrXsqT)Vr!Ij<_G<2sU!9fLabDMjko`nIq2OM@$~+iSduZb0k+$VtFu|32xa++uY8j@kPE-TzAKbr#Vzz{U+yQVl+ zVAo_w%T;c>Sn9Skd{Vw<#oqcIiGbnc0|o}Ec!p8-5assj8P%*Nbb%e4+bogWyv~2ABHo#niackgg(_GyLCt_yx zFB@v0tTwMd--xjRoKaBaXtRh&8$`HfaJtD^GiYBl*GO0Q_W#2y#4it<|jjpuVflE{`B6e4&=FvO_)XXI(RQ>o>q&GH??Z{l6_H@EZno#cgOeJHDHCJUTqo)Zm?ck3h_K zq_U0|>uhx05CPhTX|$H1&1B(Arfo3#8h-HCY2;dzOO6`nB}MN(o{UF(T|xA!tJT*7 zge%dcte3v?`Bh)47VarY5i@a^_h;WMh+2{-u>4oTJiCtwim9NakBDWusb#*T%smbb zG1Emn#P;DXyxq~w>89~=jp3ws&Qy<)b69ifC9~ZKv7bj_5`y-Z=h$V zg{`cx!KqFgOFS57&!r@!_Z9V1=$MRL3#7g}@Xm&ezu9;4^&@Bg(A9X?&n)xiCix@t z$lUwT_j|L`b)%&ti1T+eWEwb4e}Htbw_pD@rNl1x6Vmr7lLuXXJOR92>&1`BNbp!` zvouDe(p_ps^JbaTA|*aYBk{Ycbl-8erKF?me+3T31Ss+(n^R)0K1;6EzvgjQ# z0GFNz_Iep9zww5i_Ffyg*;qk4G&+D}<+b^%(Rl77`W&Idulm8+9rZCu$!9?Zr$gIu z^n4D#;Mgc9XJag8d^K_gW!Npc7!D7??;jj=^%#b_2Z+d6w5`d*X_3IEruZ2oR&Dw` zgFuJ)d0^JjQ(}vrK@yC1awb_dxC3-~oOWDmQ|_t`3IZqeu2wlek6CxsTn=3h!~|3G z`IAfZj}FyNT1|^=UHGUx$^Mjv#(imcI~bK)vJZIoYHgNlq%3l6={JdP^R7gPNJo>I zeiQvS;<)snoi7Z^9TxoGyn9DXi|kVfH_@*Hou zQ_;58hBKiL;o`f}xTj?OQ3;L(Fs{cz1Hc$A-kz2L95pQwuX2mQ#0p5$$;k&>+n@fZ z4?I(DO>lu^qi5veA>t;4olO$F8r)~AYA}t;Ph@0dY?|JLQteBBcDcHhuT0ahE!fF; z*q_|5`z58p3H<{6F39g%7O*F0U=_YjzrMC+_liMqa?br>Yir9pO?joi?4h7hubJjl-Q)ftXkb1^W$&d z7%ByLrk?%pJRl2VcqFI6_as91han;oVrEIm;InYUxB&c41ry;6PKLR=9kdvUnO zyGeFBsX9<*(GiAx#(&7mtC1*0<3zJ315om)(&N+Ps9a-$k@Ko=@mx+bEwUllI9NCl zSmd@!hL~roT)(qc^h^&>WWz=vr>SVmI(iX_@AfFOa-SV^2%esFL)^a3sR76TI;QN? zO$bb}^XO7UN2~3_P}<0wDG6_~pOsr5H3bV-T_=)Yke&CzL^LUpNDt`s-=t$e3ekPX z|K)S6uS$%Ch7oK6#*-8DqhW^`Q$j*)Nt}P~Pa@12jwdtQ)NaP1w6vIMh3O=n!{)(L zU;H8Wm|(3bj^FrVj%?VJK9^}19UU)CMqOk0OzH;yEi_IsRrRM*;eB3Q^FD^e59P#F zdG_36UMJtPm}se_MHU{$SL&+H$fE@_$Wq-96CW=D9jma)!|xmr>?a-fn4D>#(af;! zrJ8N^lQ%=DYB4bC^AV-bT;`NnQV`kg`sM*V=GiZ|VmrOn_FTgtP>@Fvg}Il0uD{h! zjQF<=3f;g84syXBaDk$p&FU4>`bSZacaStP^|`Nl($ZzBQ-UTj33XDi9d-FyQ{h+t z#qGU<+@@){O|;mSY@b|;o=h`j5zkhZW!3A7Wt!eD_9b^_?{gwyp>@&3XPmv^CJp+& zj;;Nsx^uz|A3?f{ujTJxXDUo>9l33iu4C755Xg4C~!7QEUP zvUUkC_ouJgafF8TFJ2T`a$HjL1_!WSkxNEIqz5>ko84;X{x^CFdW!b181D^|dRilD z8kEEQrY8-TZr`x~Za`Xc)W2&)w8nd>nrCj=65HZ`llIVehQ|p93AmWk5@57u zVOrQd4EDvk$z2uuue6VinvB3H<8mc;8+o*n_`lJ-_mUvFO9ge_tP4GzG8GD|?eOfU zAc_gCn~Uk+%P9G)yaK1IkD|=n$^Z9&kPbs}?Y0+TLevPnV4wZSPyc;K=OL8Y6xfgN zU&Yueb7TE?lGxUd559hT`xzdB=n`}S3(D+eVTcayuV`leb;_JIu{=cpO{^7CHIPTb$86KkGs?PUI;lWs^yx(T1tWJ%meo6N@lyRk+{Cxo%s?sZNePkuQ{U$%mS5pI?DXqTe=2dqn;J z9&8o4Gal1;j1_n3;5lGR0`U3fY0Wb0TKTJNf?!UWU!dd&Naj7FO4C_NF#W5OFn`|o`ka~QrR zC%VLXeOWThxH0YKyc1sl;1I?zOdOo{gCDmvN;RGTA5Z5UPxb%C{X=%fF%!y&kX`mp zA!P5p$= zqN(ouE|DShsjuJcU7TgMRn-^qIXlS{K^Htu#8J{s?rUFRkS~c^x~ zOi^!=)5Wd7HZArj*UdO)3}}^Fy?#FH9VM$X{+d}ryX9vdbAQS4T533Mu=BC}@I1Y7 z46Ove@I5uvt$_5)%J!=p^y<3r@!{W1bPcGjyMM9baCsYoJ?&13D0S#Wo2+BEUicic ztG(O#MsHl9)6#8w12+#r2Y~liE8ZD~U)jv`*z*VO?uzDUV>etTi(Tq3kJ|Hpw@Pqj z+6&(t13xFTGE*X}dV|mFb*35@G1HKvB)I=? z%IV+sILcva4CGU{xSat3NE7ZRLHH19@A<`yc)tKI_2VP{42x<7njSIHh2^qk@w>kx z(0zW(CTuhO@B4#9VqIgtHGbsFW%b~7Rgwzh=7WL1P7=pQi{rDl1j2}y@}qsSvNAEZ z1oABW{50I(F_ePI1rHzJxxoOYdYqKhb~SpK4|8D?HFB+=26t|Yc($W1pWf~Re3c$NP(Q>X| zz0!aBX#;x@KW$Mk!%$vczS)1Xp(w@LpglNIO>w0ERIfaKsytKBB?hVY!KAFHh~xQ_ zd={h>iBir3_ht|WMCQYk1{(`Y{LDom{+U0oqb^*>jU`I~uFwAe329(K2%tWbu)A2{ zI69mY0IvhklGt2p<)0{ts-I&Wct32T%pg_@!6GWwDqn}kW&7^jE%CEbv)z|mhIPP1 zdG|X}gNUf{&eU%E>Go);anGh22qwS-4(S@qOiWXo|N8s;i6p#QzbhQfC`ngE0$byz zn2)`c)!l9#vX?`re>sVb4dO;+yU{HzEp^!`Q2PINYBeehY-UalY;0~qUU8V|(~N)T z-2@q6e+O`%ks?A&|ej#zveSCGBL%{@SXfC7crX6hA0bD(>#&qn->?2Yj29ynCUf; zN56kJzLkk>hK?(vTv#3ezRUGSZwZJq@-ntq@SqfK^O#vY4tn*`Ty~E};CzY}0$a|{ z4@b%=Htv|Kg755i`+gHP^JkbEcxR)g95OLE(olW?6}Tfy7{#Ws^W)11BxDg>zixNt z`luv?1xLTuesi{bqlY%2*F_XLzxS$^rl;WDJEM9Zk00+%B~5qUaKSudF`uo8#_fWXbz-UU^10QeCBA3hB)MpZMoY(l5zZmr^7zR!ZmIcXD5|z0yTkXf zMd#oxUvMZI!exu!?V8z=jryLb79$G{*qRA@$eV7E0xqQvBRd)*v0Q@$k$JOfV5Dm4 za8m*rKdax`VFUX5wg1mtz|PUGF$miYko9o)v{Lam$sE|`_?-~TJlmSaG$x=HUp#es z)FFiou4Gp76wI7^n)7FK@rB7;dqhGZ1_s7&H+eb?vei_VMOLOcf00ou6oqSAYZ`ZCy zWj{0|G)FlZ@A{u^Mvogitt_svT9ur zSv9LHMzoEF4Lb3cy;Uf%V>&8p9hbJZuZwDwtBo!=^E-WitQk#G5S7S&GvHvIe7-#^ z&;8Gi(%=a3d(G-)AT(}G%i%>qs-@vLFr@U}>pT?FFEy?l1Q`M;7xWjkGaGJS^3Bst z#c@RBvqXlOKA~9XjWY*%djdN}$2X7)8{15vqm}8^oD|5sUgv$k(EldnGrI5L`u7_B z*v|cw$mDyU+?F^IlQkRGG~u{;wEjn1&~yPW#N=8Cie$(@2nyI$)jN;yFoYibo+PlP z5%D~0Ykx%a+m`gR-0Gj*C`OH&O0p-oK$TY2bbr+ao;&suGaO1%DN#|;D@F6&XO!ko@zKdJ z9y$wW?cZR3^0aByyLQRrQRV{5`yvFmwkKZ^v~V z&(cISqc>(|=9R{cga{aQAXwl6xRFS`dKmqx_dTSWFORDb!#b)eFB?r`xvYwV9&kFC z_30CD?jP%36Yks8thQSCcR~?#naq|O?WlPdPL+{Qf9n`rWBoSPsJnh=XQRp{NN2w? zTkMPGpx(@Nw5+z0+_urvyxn58)pQ=9>~~^gDm{BO060^P)wvC-Ygj&2J=SQ#`4oLY zNlXW3YEds1K|vO6;dhBH`4yzaqH-KpABFo{Rusjv5KFlm-jI%? z3|$lqZRdU!C>RThqZln*9yqe9Z3^g~Z0X>=Ew!SLr_Vc~BkqlD~{)qXdC27`y{(k%@oMiHO?&ktR z$1WLoJua24v$G=|kH$SfD~?J*p-{hmq#eWKAgY%BX@XHQx46^n#w)D)(NMHDkmv>% z5=R;43ja>F;U9nRdsBshIruK=!?r+rC!00wSt+{4n#=JvsL{(aD^#M1DI{IimVK01 zm%NQWm>A~Kgug*V?qBHFC*?wAJ4yn04__)U%_}7BO(t>{tUmCWK6Tf=A>E^E!8-<{b-v7L1{jt zXyiC)F;UsdVR*8Uace4wuoHp*VRgk48XH|v7jq5)wBTKObMO_D{b_MT~>!AJ3ue+$TQjK3a;44YrayFT zA02vF3!sH8nWyvdSL;Os_SXrQFKJ8!{{zLB?2yW*FBD%}=(tIO9h@o1dj3&U)k zzxh^E4p(OD{My>a>3rfRtB>T1+1c2%%9;`qEoQs8@f&Bx)|0fv#b*ne=#P!8^tL!|ud#S#$};3snI>QKiIB%? z;JRMd$xu!_L=;MKQ*x;g2A0^;=LONe_}gCRLJIzWqh=pXxvD?2)hnnaHlM?FVyfG} zJxeI*i$;n!gy|%&rT}C#l;q@mp3_HQt)6bl0_vfE2e*4iF4bdc@@LKp{ayJ@AmcIH<(3s<+ zYib%E&Y~kwQ{^T15TRH9uwtnNS~NG3<(|hs-<&2uq*tJ!%NLKVeQi_#wvXaSU~t!6 z$BP$sfELQ}J5$|C&&~)eE$;T+_E?{+SqZ208?JS@8mVWX90#(+nECk&N7*>0l@t@} zfS%^DIks5Z{x+T0#(lB7d~KxFceBQG0W)}J!nUR)2_zd6EBh^QQN_b|m8(}MSdd02 zM~8`GT!!(tzNkE7LSNKKxc^1VZ1yY+az*`ry%WL}ip9@mp8a?~yWQ%vF&@yis`?hj zEw7MzRqhi4b5ARQAKTpE0ZxD)EBpQ+r7u@6Dx4}eXz?4Jn;Yur@Pt@V@fRoPk^oEt zjuI~@51=&VchIv*cL7Y0|2g)n{GULM0v|>IpM7Y@+OvuLiNz9D(Qm(QnMB83_r~jWfBsaSaL0RSIB^YV_GzF+*f7n9djL*fX6)q`Da*8#2 z>_kyLB_+fmh~Muqf?ov%=dT9SA>m*R42a*2&w0AFh=_5X-TTfvuuOmcZ=>X`Y|O#h zNT1n9P=wXUr&~GG^x#Umzp?DH7pg45W5PhYPt-xV&ybJB5}91YpY<%ez^0*#;U*Rj z{+*nUbNCc|_#O6C54^u>1W2?#2NT~Yulm;&!9m{srO1P2(p~8JMwUgyKf&Q!d4t^B z+5Jx(L+g%Smu0(e#dUgm%%xMeI<;-}Bz|>ijnA|HJy`eZvPyK(-#<;ee{pf~xCqCz zgZq!?={Nci|J36}Wr=~P=xDF)nHaD*SXHaalk1+&ha94#1E0w-mWPmtC_~u8RmC~? zj}}sL+ei$Em7lB*^!i=mh%xUfWgLGfVANPq*SO%#(iH zYd7ZBY#gK9h`aPyRA-( z%Tl!tmNC7dY{o4H#3dF4ZXagCG^utbdX0q2uPPeY3?b zSDLP_#9*c>-CZ&SyRlrg^-wu{(cNDVGDtEob$054b^ zARi!(v2$uU>8R5Mo32jX{0hvk_eCWf|Ev<ffXBt z7-uW*G_zL*x)Tot&}U%gL@k)x+QncK>G)AcO zf4d~%xp)1t!j%a^V|C4gziA^d9_xyvXs5BR&1i!0A#B8^d3l)Ey8)_tWKu_cT&>@D^BdeBTzjfn)7#q{_qJ7IChXb5E5eQzs(hQU5|FNR&}~klz!1UK zK^IK0;aJjpuNz#=JucfXe&m_=5Qdq)@*I3apYt~s9Q0!R{GR=*3z7K}f4ul)^*@@` zM<(@saU3usN3;QX7j*$!9eUvU@Y4t_j}2f#mKxLtv)h%FKyNW4g(>ts=kN#9^X}IW zy=^rM3M=t^GL00OAjr&#p%mjiT$4{^DYOY&A9LWJ`j&T1b5w;KqKPSG=31KZL+)`7 zm=RZ3--5*)^RJGz0<^$dgPIL=Bgk6NgNfL*xB??|0*`N{aCqogsgI zxS|M3M<)xWWYaScD(zow9P@325wA!je3#EaN@P{pWHGw|twPzyWJva(N#&toVfeJ- zotbio$N0k+ueGOYd^N}MzjqTK`2XaD_QiDUN8&oDDCR(|SS~QvMq^PZcz&%3&^8}E zHfmuK2@b%b5#2l~?@;;!rNV&R=V|u~pnv%MV*2Ihmy6;X!}Wg0t|jUse!ri<1;TqA zW>p|4)@%>)Tvv|tiE8U>XQi|f{!QTXHZ^V6WmQsk@(sZ{0AYgL*7R|DO-aeU0n&-R zS24?y3-j$Es^3*X_3vVN4>E}=jT~l&_K9u@YZU8EKx;cJG!$fmok@9!;9$(f;E)iY zb59mJl%`Pev1s1EO|MaH^%`QiChe~nqk(E|lyGpVunR&9dhS$omwASOfZ*WYKYt6c z22J+?H-7IsAXUnw7Vz}jn`wmTRJctI08mcX7SNfRhx7K& zpE8?~LgN-c*cDmt-?#n!%Cz;e#S#|Kct!%soi6JfkCkK66txflO$GQ51w7BE1Y#hQKf9N8Aog7fN#TRM6m*ll0?IHoR&vo>cT5^i>3BQ82P z^FOYuh1ol8yAwh^c4>NB5mse+Y-~NkTVH?BY8MA0f4Q#3>Qf=5pzpcSQWszcYz*-a zG+!1#Q1*R#{k38QN)~x;GqFVthX>m;;^6kf+riq)Js9;6xcc=es#V2w|b7C ze`k|kq876#Dke%MU2uOuptPzi5TDXqS8HFXc#Yq~atYt4Z`jRTfG7O(XQ{i&*|l&7 z!LtDJMr%uJ0$P3z?TEF*-tiT-!?!M$Yh`LczVZ5dKZ;(6HDcvBHUn%?LVx@>|MskBQs&UuNQu zPxK+!($Y6{KKdO%ruF)0sa;`$VWVfWN2yk`&vOyiRS3EynEK#i4$BcCji~ePuL25= z^cyTHz+F@vahz`Y{?_-fjqb8oy^!nl0F}(4kN)!AZwp0r9y-JEN~6ZHYT3iy_d971 ztO0vj;Ku5m}}IfZgcoaY}MZu@SrsBDhGpF>$A9g*x8|l$6-{a1L!~SSh)(6 z%Mu=OC8Nx_DKEW08PrpxUG^1cE_?P}NWU-&Xg+^F_aY}QPWRKZ1-QFZ<)Mk>@u(=Y zAv2Ouu9F*weswkiCv6kw;kdgzD?RTaha>_Ar@*REx8h3&+3Eo17{sMQ;P7HzVmwXz zcLNeu^jt)ksS1IS?GI--lU~EZ_k*jbn+%~0NOE$^RbfQxmT~Btn@QPD1n$C_gyA78 zb)1I6q-G#le>Q!D3qs^B1u{)q74kPFC7=MVuRJp}XjxNoa5^Y8DwhW{^Vejy#gz^1CNEQ3+HIKM zHN3VhQF^PACq;TUnoV8&Hqzpq^`P;GZwt;=78VB0t~GWkoV%7$!D!%Fc7U-YrMZy) z`6h}S?)&4_x6je&1X8!Ia-7^j|&@W!l6J3hZ`c|pxvve=)@(@hiZ z;OVQU*N_k0fl!s?js_}-rk)m&IXvxF^OEi6L27#Psb&aa8DO?w|^i!mwOc+gxUq|j_A?7jYDCFihu z!l9E6=j}&Gp5-$78@JYHl@K(|@a~X|G8m~w21ywK0TTJ7vbF4y`Ctljq&(y&W8=Zs z93|*@^Z1MV{bwau2&JMCTk`EujvCdZ*^x?>ir_>MMB{)AnXP|rVZhXq`>yEr=2AgD zzSMgZDy7Ser+V_KfCn6xAT5Yf_oS~l0rw-OIo$d0ofo7C!>69EXl7v{$T-w20BCgm z2K#+{*NuNbgGRK0{iHzLUp&l=#c{?FG+Le*EMgoTYwp=1&nv8BTN+GL2LZnBbS+Fm z+brn{DzVrVneHK!6wq1bNKyM1Z$N!EQSK7PJwqAf_}3U1fXY|rvviNdE}|q5Dc|J9 z0~sy6jGdcWB+5om=oy@M*F3z2oX?PZ06`{sQZKKD>I>x9eWuu{9~KsZXzhz`kx~{2q=TL< z7RH`#{oGwNR;FB8TeG>FI%BsY#Uq2-o>r>QAh~%nkAKfB^O*yx;x5dxM4&zx zCrLrc5_}EORlwfjPxI&%~4*>5s^12G?b_8MFwBc>ot2m)5w$TXJQ;|HmbZ&%Vsw}bv3 zkGoW?>4~Yv<&Us|CIN8x+azB|6ob8uid;9>r^Z$xKvxTTTqI0Uz?HEXnthSuzZW;- z7-#7L=Ta`?mivA`Q&hhbW7pm?I_cvAk|G|?o?Juuc!ee#FE1GjuhW?QYKrlIab2C_ zl=dLAK^C8{C!jp>$Rn=>reyHm6j<0WPUf^bmakTqy^>+-wD{H61!E4B;3uld#g~ki zWQs5v=GBh|;b-x`H+JfZ)RgE?ov5+1)=h5B*$v6bF&@3A`5q0Hf#y@ci}liSk*59R zLYsuU$_eE4svTh(MbO2IXWTWaQM3r#&gk}dIA3Ad%y0JlJ+u-*PMt~F4Tnw$Q`5gM zF78z69tv7ru*Xh%l&-YAZ+Ja@eR&az8|pE8E%MPHw)eD47+>WdJ2l1@SiPoV)2fJO zQOPxF{|PuJ@NxCK)W7|jVg81?`W*`%J%%8+!rqGBH@D0FDHQV@S}0`|Lr>3**ocT@ z({3V4`VdOMRrGGUn*Tel_ul`f3Rt@Sw37K z&w$36)mZENJ-^mr$|M>MzqXSbjrIkgk{cM@^{&H2>uIOCBevx^P;_75M;4!UbWUK(NIp@E^p*aTH%+nX& z`&1-7pLuMIwz$@Gl<7*GpKLh684Q1jJ!o}Wb1T5%dx@rsBv!g2-o z6Hkx%qfs{nMA{O|-h?1%SMtg4kwGhRym+Jqf}L_rDYc@O_J@2yfB99Z=4qN*mPmlG z-Iy{|9~0d8F&!XIf}q9RKWfJQ^~5GB(}!JVU|V)_ateLJZs%b;-U4dj=$jy=o|430 z>>kJ{*K1a*u?6d}M5-uOLxtVM-!Fx<9f7&IZBSvs;bjWn$3-{`U<7~`f+-<44$|xL z{P3}y!AI95mdJth=FGV41<%I7V7Th+ef24T^gG!7hla-_Pj+cJQQ3U@cXOoCT1Dke zQ8-ALmfzw8&u=bu?Ke46!n?6wZZ9oajs;>NjOJlq@)5Rl2|G&=<21FqI-Mxo;h{bX-kI4bwVz3p~iKQgt@ z2!bj|T3Qwp3xg8NGzxz)z$c2{@nj|0iv4pY*TS^v8+%Dyn18mV;cog+Em4zbQ%vtG z-@lR)UJ}SWnZImGpwP{ZqvO21Ts$O3H*B2ZkWp@lz{sqAlp1%wIOt3)U7U~jtgMQ! z@y%ni^NTu6Z(qkma_YT%^$@>WE0i&!stn@OIpOJLXD{>GHA9$mkN!^WAf^LAZJqN1 z00E+)U1BMpQmezLIlxN5j03au2&e-04)@t~@6Vcj6s=fZzSCp%@f9!?YjIju;DS4OTv)B>U6_}3J+mBq!Zq@?o; zY}(Nh&1&uKisI)F5N(dA75w3)&sKF31>uCZAW%#S#w?Kj1Gs6JMfbP&Rb^&nZGxH5 zwH_!4G^C`93#Q77iq(^4o*4uPGR-ev`{7>FB(vNYb1+>n1+T;2;o(HR+x8o1%Ac4? z&iK^yn+1vv^+i_MtyFYI-Eh#dgN{QKIfohYj>r@wfr}S)e6QODNGO!E5G_$|Q16O{ zcEPNi4TRs80V~kG%t7S+;-bRQ@v&junwS)1gRVu|MFPD{=EW)A(oE0WB z!XE4t*X=Odj+}l*bTT`Ul1U;uB}6a-k@NN+EXD0pu|1XP{zE-QTjRy@^tfDB<4HPB z8GiYE4XY%Cz0}*cWw`|ttGDW3Z`iNGcQIZFyh!~af+U`5O}ANw+}@ML{AbxJK2S9( zVdUnrJ|6jG@a5t{AJHJCdGPUI*^TY-9NTgjI|W)lUe2UB?fP1X)P9!T?31dU60;pq zqZ*&h!w3E>2Jcl&OiZLc+k}1ti4$Py-fjq`0f=mvCVQ)}p<-Xu$%-9eg#FHss~wp$v&5zJru zw9!r(MM22TqI3%(r$vYiL<>X9#EDesp&UvR*+$JJ_w=NEnA+@#xkW^PdsEO^(j>Qa z!in;_Pz2Ju75A#^evxxs6wN+Je`WqO=+F(3ihf078p4ZPxr~h7qg6rdMV1rwH2ijO zKM2KVkt$~0mdGqg^j(rj=}Hs{3|9{LtWR{f@AH9N%v;OGkSL|@3kQ=$j6B)P5$nO* zg3jDXJjxpH`qJ9G)C!51u6~3d*lR!mJV#V{aAE zdlrC%GG$-e%4{3`6H>rdkZ>fZ4aU@P49nASeh>)Tl_GAJ}*S3*UT1z{Yblj;-s7!*Ha0)7?|IR28;=N|6Ub)X` z;Ifw$PzYi(n*AKuXm97Gy)wx2n~2o>nyx)Yc1K_^9QPc*`#~xCV^%tRKE1Z%x9qs7 zAX~|13Sc@4Z}*ZXXt&3`Je=w>Mxs)&UWO&rRr_#b%cdN~l z4XP9`{$YvkR9w#JR%z_E!y^$(%gk_V-?^3NmRCj`B1yhy!?;rUlAcw$R|YOGtot{I z@b6uzMZHYP5ut(WCI?~vUbJ=GAiJ@D?MmKlEM?NiawUi|awbB2Ht9u@VBZQ4GzJUk zrGSX`)w(H~;6upn7<8q9<7z-hASkc7JbXsu$4&M12YN`&7r1TVq& ziYrAtYzZ|H@bY?C)IRUZpeU1S{xaxl0bUEf;PDSTpam^92(LxR0%>kZVurRY2eGzO zs+!f}n=(mV$+Y7zb^58;``54DE;^tX-DDZ#8iEgjGzsPIS>nGmmq)@sA0yPZO}8;*UJMcIi!YzYZ;etGeGg^@RPe5hHF{x@ct z5pV;mY;l7T2w2Z&8oZ-G#Y2gt*8gS}M6GE!zvODih?p}&9MyR)OBIf_dk z&`Q|$KN~?)AT{f&{9arHhJ~*051H#A9%evCkIcWUKDNT4Z!E$@kd5zCMc6z{WF&S& z;JhM?%vY1oDR*~ND$WZkw8goTp@@F8Qj`MZ$S0S;1!iJ8=k*-3x0`b zg0rM&+WlR-aYq9<+(Oc?XW#vyT~Is1e)=2QGMEp!@fV%O(Ej&ON8fOyUBVRKHTwl4 z`KIP3g`|6!fqPa7s`B31jo$lKP^)y=g2*h9B{D$z2_I@!!W%ObhyFpp5$>HcpVRT2 z1XExOk?AT!83jl?5-KDm@)kJ<&d*IXVwoYULGLX%iALNYy@U6-lOozfI1`&F>z5!W z*m{jK;c5n$jsB|t@#6<Bssy2+Phwo_pQr|2NGr4xfchw4KmCNYT(8Df!{RZ- zpS3><4>&TllJSvQ8KiwcbagLqUySqMcdRqPiOP96Q4X9US zS?p%lt6~wp=W|@O1a5mPUXC-})03RO{=1<+?T5c46!{*m5#YdC*#N?IQ2O3sVczn^ zT?~+qecYofaa-I~5Wb!o6%wgZueJnG(K?xf5=Yd@cmcM2F`WxFzvJq;Hy}WK{Du6S zhr8aziUj;wTBvc~9iE>zQ5?}(77p1BHAeOSn(`oz`;(=nL;{sRkQYF|YB) zJjRTnNO_imhKd#OvWg9`Qk(gFxx?9Ia^{qAdEEcBC%gF+%qyGq+r6G5S@%KM zgmOBBdkb=wbvtHCi9T{!vMI$!D45k^exd$X`xC{ixoYi;&A8E@KZLpJrSd9~c3fY`0+DtVlR zavicRXy%26hx`5%VZ!Y2=gYSOjx7kBf7nMKX3&GFY~)+ z>6DX@NP6599WuoYT&9f{(op(ZU$=Z0L8rr``B1f{X}Es$?&kDI-=Dpvl#8l&WS_6^ z#LPHe3yhBLkAONajo)(TTRYf0V_!$)VR*6Khm*=rbPY`DF&335>Ca|cu3x<)W0! zOC_ev$tFv;Q`BV1O{yzGdznU$Q(wS!TwG$W_QVp_0Bfu_c z#BrO(b^!uQTkAEYeMzj^rB;fHecwruQejHV7oq~kj;=$FVvX*nIv!MqPUoy>m8;dj(ulJ&;kM#GcXkRZCp3!I z)C#D7IDs6>rh6YJ_=Cqr3>cYbv4nOX|Mucv$^}~Sl+rVUQA;bCei^Tu8 zb`yuj723e5xhcWjdN;nSvest6bdWA=RQ>Gya0UnvTcdBs7dpa+j0KfhmpGnXZap$= z@D%dh0#8Fx9yKSb6zt}6N|*fn{9zqlC#X4S;TVh`KpF%r$u@IOPn!-`nokeF@nE^# z()^a;Ewmg?wZc(U{LkwACS0R;>u?wEDrUd4EeW5w!^UQ}{;9a5g$7_0m2M=$&Z})J z=f156-6AQRMw8V6%xx=twm%@lQ-o^|XzXZ^gXKQkNLyk{89H*5 zF@fk@*H)+5$+z0EFqj95c?Y*vrJ-Ss{3yLK!ExEXHS7{vnvqC`&?oU;2kSMD!iipD zgnx1zO@F2VUDZV#dd?JfoU`7AI}*pMqyZ8ExgV^Dvi;9bYXZv8p||pnc5#e_46xHif_;k zdPKwxr@0nVYHE(Y!+;w^3jdae)<3`Ogo_P{0=n$K=FUXST6M0Lw5D9&5`E7b9BQAw zC@9w2Pr0sp1zRk!7CbIe@Ynridid;I-frd_8p2J-!|ojWpM>YHpYvQ;+dpf~N4j4- zOnsWEw_EvSGlIePeBpDK3u*W9(Gk0Tl|49UOOq?{iHK-L-FJ2$6&cyh{mWpTLqJ!y z?Q_lz!ds%i#?vY@;Bo!Y`}rjvmtm9pcuZEc^|?j$1QtGx^J*`>n+cl#L-4V)YFA93 zj8aQ@ZC}$(1`@gGpWDm)P9JqZegi8nB%ZcNkJoZ5_C%Dlh*P6!mzaH(tGp=Khg+M3Z5-_qST_ zkCa9`I^VhmhgJ!<%DngN+*|$7 zK0r#PL5s-(`uD*cF<-~C@kKsE>YoAgg#;P3P7_+iW(5U($&vA?MYAL-BavV7AX>HG znENs*xw6c-(Q&0VtD}`F+l$9#J~!z3b|Ivj?x9icNaR#oRLdQ#k0~lE8&`X7CMX^k zDsZrk)cXujsgkj2mz*6w@3*}DpDVHLx`9rJZ~2VfNNY}u{c%+lxq>eHaD8>ehCo@R zajX9qwOweiM60LF=mE4GCdz+3iv;@Ju0U!&1M6VWCc&(_1s$mgFi^mkCEqkqdD8X+ zx)Pg{FL?-K@$J6lwgc~ggQBlb(dggK$FGDA?VmA4hd`=Vah2A9ys0la`iU@0xXhrY zO8#}^l^9nOzIJ6#0EZ3_5*p!Hc^JJfM!OcA-PeEGw9U7K;TQDvFf%jLin|_Je3nD} ztnod(iq_UD7WVv0N#sPWl1d?Qkfg2eF!xccH4PawwW*~!YWDGG8NXD7&9h9Q{S}22 zuCxLnP0iO4F~>UzhyAKhCOP!K=gEJ4?OKIF%kFIBb{BcH-S-AoL?=bW3CreNh%Czz zccVi5Z;07NF`)l$gs-jj&wDFguH-X4e>n3auL$x*$N(&G*{Hdc6*4o0!GhTc+rU z;I?x?yM??*;jp9Wv`7g_k6l;^Q2o#Jxf>iz>{)L>Ks8pX)A9>;!HqMy74Vmnv!xUj z7SuY88zF_HaL^y)|5`9TEjulOZhgj>Lp@-w^LxRI)lEq%Z@~FHPs#<_0vQFFc<({Gch&g3mC4`!%cNQ#rSssQNwL= z-uo?wnCC7kvGwz7qFX5AEgG=$)p2U4sZeP_=X>9=@b|Yp*!F0KEUm4twZY;>CS9zO z7aWe_5b024J=m+hy7v3a1FQ58=$5&ATwsE-;0!hyIPl>bwwN9NyEO&f6&(7TX*c13 zdA>7phzKgQV~R{cVsMId444|^N_q{6&o~+@7@=Iv_hu|~Bnqrg37frlw;Da|ftO?U@v*dY^Y~h`(k+qQ%)sq} zCDUl0Sa5`tl!TeG8#PgKOBEvfcR`fx*(fHEnX)J-NoIc5>p)F!@lgjURT#ps&BK{; z-IQ#eRuX4H@F^=(A_f+Q`1DW|)%?cd#>R>-971Li8{6B1(q#m*PhpXQr&_Dg5~i8w z*nf%e@b8CsAOcssLs?~BEeh%;7e#06jb!G47-q)thF5zga{pI)p=osUizYX=OBJcd z5B-i@kW%m-fmgN^=$qOsGiPK6)zt}RuLQlkiT~7EVT*;wVD|0sN+`;`tJt8G!(z%-T_94^$w- zR@KamjF7X|$^7jB+knm8zDfJqN7%9Wl!A1;yzY}fY`UT-4tEcfo;Llgcp-vEhK3#~ zpUif#n0a%Blrvi*!2fV|CMtz}OX5R-j+ai~*Z_XK`e7J}Jb^803QTMh_WJ?Zw6`e= z-`_)L1c#-Z)esuq-|T|hVnY6F3k@=e`NhRYxXs@Cce{VG|BU5naaqJ-`kVI(UF`n7 zDj4O%!*c#&f4kso5*=qSPt5Drn26a(ey7=Au8pvlc+Go~sme34T~6)I$-KU)34{KI8HQeCJg4J%1)HQK?5qjAnyCuLl{CU4Vw3QjWA}7W*Z7qqPdO-J~;(0 zPxqPC;50#KyMnb`8$K4oB$`axZtCCYr%!(|uiuEF;wywKw*qoO(c;IV#0=UuKT>s5fS=9_j>wEMTwWB z2pp)FZN@%97m1D2xSocJYO=~(p=6G&3{ss#Lr03LtEy&w51aWbYtYdHP4|~M2*a=4 z0Z+z1TEQLolIvp?Q}3kT0$PvXZnV>?=7rE%Uq?6*%6^9ZP(~{wi3DQfNN(IfdG9Y% z3pgg0>)^Q~ZVzV38+@`*g`=#o%}A!&$%g^z^Y2Az)U)bU7IKXiaqI7dge~lB_C`_B5kyDiIGO zbj4E3v7xMto5Oorj1~xXPnvF;>TL0cC$c6~(^IuJjb=-cAU$mv-b>2wielj$z%S<^ z_z9w-c+e0$>Hoag~mpSVmkJ!|RWF2kL$dz(6{El9oGE}Ru}UPbh{ z0O<)60fShUNHAML06L3tZBuyBLAk)}WR+!KAQyC^4chtA$`VYv)ueM?fgS& zdX^HN@AJ<|<1C55WY#ohQK&<)M7vZA06WZ(t09&xj z2KYEY?z9qLpdfy7)B#c!lvu8kBy&8So4d5f-X*I-MLwIGoGK0`dZ zVYR0s1e!yrp@99*4>0h69M9;FoC)cCwonOqOvN~4HF`Rp+Un~j2N>;C7uw{rET1s+ zuW6DoF$k9_Ztw#xer>kew=!Go#fx*${@wpu3ehDf`|l)Z4cbSU?IxuZ454Px5*Mr2 zq8QIb1KM|p?|`XPuhgn$qBOYAG@yBtlJydh}^{WD_q2iP5swE-xtn&kN7NgwQJRCC+H~VK)+xUDdV>?~Ijvvr?aHqE%w)?^` z3tUVPW~_c!$>CDZSA9&IHzl8?{w`uJ4r{0H;XB4BpL=%At77jlC_1PQNUYHHr9c@agOpBDlJgzNk~nxx7n=kG9Hjkzh(z3I8p zumBRoKc%Js4XOid>_CQqd9;YfAB|keAYj-J({Lm~^0%EZ0vX~zR-mBgGx@74%GhDN z+o}e-ofJU%{Jj%r*_Y^AuOnfCbti{V60rlekE)4Hm=Oxv3@0jP_|H{aJ2^Xl196b% zdyTQBqwq#+zy}RUo~$4d11HCBJ=43lUs&lFT?AS+1exPvUA1ICIaBTvkw>5IM>Q_p zOnd(OvNik~{kNLN8MD8?GoG|b%590v{{N9(fC{7jJg0Gk>%rtGd^~JSEJ%ilgaWG* zRGqGteQD&}tOf9xsc6=?7Z1Z4NNE9q?zq+@@R(8y*wRo^*0~Cy%KyFrex2*#W+D#PE$5&n^fOVYEAHtC=nA;!`Q!d~&#Pxs(#J zFNbAy>vl{BXfI|1OfKP93zE{&(E&#Cy~IkB$hG`lD0(h-;yRc>(rj3gXo{3CW{D&W zH}B{s0Qv(&2H6h3!)0z7 zbMs8X@#0W*(H{5*V=8{SDOAARcXL^YHI9j_(5lh1u&A3F5Jr1I8Ee#o1ZFq5g8!Y~ ze4wT_40ibw?;Imgt9?&4fAk^A<|_-{0M0e#|?h zcm3Bf54?0D#$C9%hPbrFY@60+lxstOcAv-n+8f49)~{4 zkXzq?1d~@WMbwLqPK3|xL9L!N62kJ(>E=C4mI-a;e+Cm=!~FIzdHa?V@k2x|f>II_ zRmKp?0_g{W*8{N}QC5*HIP}>oXs|d)y&t`cAPU1TDWS(mgid`EId~ESuiqe1?D@Q7 z+M$qg?P9MM2Rgvmw6=GPY(Zy3=OmId8p zEIn0`<(Wdv4H;_knABK+3&28zk_6qrxQ>Ny68LA>D|?0MF|P_4fjOhD7ed8iMgxVE zqR2o9S}Wn-Bzt*)09218!H3DNfmWxv52hnMv@ekQ-0Qk>mjSA1^OvsdY!4Yycs8$% zcXwsBCcSIit~|Kvj|N^Ee~TjA!9PdraQ%ZYm-n5Du6BUKw9w@leK*}5Qqj(?9L<@ioP*mu?MS{VoMIC7?J0Ko1_&y+T` z^U46Hx8B|{@iHX#%>%>Z>^^RM>trkOU}#aNF1HR~81qG?Vw*<~xTN1HN?cajKIakK z8ltyyvBU2kap!$r0s@H&)n?%y7tW&3&6I4Smp261EEWx@SL(+4J7-1t+_mtp25c92 zSoBs_+!=Tek|=Kfebe?xJfSIIS>%EUMwHFj&Z1JU0(!f$1n`&RNcou!_&w!DBU zyutRMq%VbwI&4?IPn(L*{>M>4x0UI)9_b&Vx|9CD?}(n6wMgvWdb2$wk?9Ppl)GZE zX6Ty^r&(ioP;ad(Y5S6(!fO>Cd(ryX1{Au zrKWJC|3UJ1Wih}@C8q4|^AX9mHobEg@O3|UdA|eiYK&sUj)_1Z5)-I{7{)eJ;1AhC zSP#nno1;mqAy}MgTuM$3)Z5e%R)uEpSFhzXggwcBv$M}E2r?x0k2uG7b5f!oj;=ng z*KIt6zW&vyZo@&@i4}`)^(+8s5_T zrpR%5pCvABO6PD*Hs0w;=$ioF z6MT<^zx7zK(Wuxy^{&=ATbsXX-4Xsn{dTM4h0g4fjkNa!o4sggB^c!jpG2I8^C(RXRVP<#PL_xj|Ua ztY36flD2zno_b)yF!RB1%#ydn4;Alen}c+oX0Im;rW`X)HS(Z-0nt!1Igd0kJuJeE zbe~i|e)SY`2aMC>VuI~bTUwpiejC@$Be2e7y-!P7DsD?Ltl9`?|&XB}Px&>8`|}+Yfrj z;Q4rQ`1RTFJ$xFKleziD*^l(;1{FT7;EsbC1?siZAJDJrWs=^;lF*AXs^oJ)sshmvbVo&!-@PLN2qK6(A!4V9-X!gA z&Y<19@``l1*fa=V(k;ts3YaQCyL9~KOx5me)0d{Gpb#4q#X?Sbz>j^f((Y6t4-Lz7 z5l`itIl!ASC?b`q4U|hYs!L7txIFhZM$X}o?jId>`q#ba{TE^OcY`!Z5!zR=p!OZI z<4ls#)8^uS>z&a(2|bhT-CZB*Kh631p7HU14UI~vN}Co@ScHY{N1`;M6JOw{DmER@#qL!&#*+v4SA_E+Ha6 zKlPClz#>+G2c0eZ>h=AdKMTwjZ87#h!HvLQv$7Gmd+%OfrpiC>jCp>RySQb~sTiY_ zBW=F`TMZjk4*Roy9|i`1p{22&L!Nr>&Ff`52$lW=BL(7pHT88OmGttZsS6E~?pGz| zBMEN9b`J4I8(Quq6uOpkw7u6l-Q{DSChHGhQHp((eMQme+YJm45Mn|!jXdH(r$%u2kCW1-t2MC7Z?g(8$yP7S=)U%VnbEN(490pL1h zjItweT8OIR4tW11KfhMRZ~M}>H?^6iTfqK$^+6qdS5Z3V&w}0Fr_;}8=NX<;*^u9OA@E{AFUKW^=w3wfqdc3ZUKOJ`U`Pi$HNo<9iE<+a z+lzSngzr=?{8Tn7nMz7ZKdHLxAM&y4<(hmK&Y0g$EE)%i#CWy$5J#jy+k~Ps)mWK@ zr@G6o;wy?|x{u!T*d9RE@ z!b1v5%JC|LBe*@>*QS!MNt7!qx8eE*9UbTI-@^U(f_hVH5Fb!6z5{KE7RP);uquj- zIX8cGm!v={B-A@&y4<~>In$)^^Z?Y|T7{$!4Le5KUm%&HvLGek`7CN1KNC}zdD-WD z>#m*n`#*pDzw}A4xg~Uk(S!N$?#i3% z>%)|GL`E$tHufeuiW$Gl@iI*0=H?aQ#Q$dLWU)6D;ypoKahFx35p1O-T}gT3U<^Wj z2x_^mhG zyE8}s@XNz0J4i9Ah*U}Z)39VjO2=Q<4E_4uO zH=S7Hw59!I0_T_I{(EN_M4!)G9PtzMC!g&tm(5E`hA?Y0EqQziX(`dK%0Onz&nMx9 zJ{LCT-%0}Kbi1_NN}Z#=He*hUgt|*PddK)F%T*69eq zuOAWk_E4(Yky}W9zW*`}JqY2ZN&>2zRmc2%3dCQ?n%4HB#(jj%h(M{);0-tZ&9?cs z*1msJ1O}3*1U;QK-U^zStsfdlP%t=JKp!MoUI^wBhrO+}hZv(D&0<&|p)fvWB69fK z;8J|U%6#uN0*&^Uq4W$VR-6YfkkjSoSOfy?*s|6Hq4#kMt_kkhwUCOds~D_kjW0#F1Dn4aW8)MU1&&-)i_;!Ez7pj+8B2dKN}fy_qyuUkn8so zn|}fQI7x_mH%;oxl$DXOOL4qB)Y4ku?=&2BGs?T6MJ$7ShG^M4kEd_`yYXm}qbd3F znYH%t*_QkF_s|DwhZN!o#~QQOfB(J*4H9Fq?*5y5a*1qbReq!8b1m{%a)l@xD^Vawr8UikS92^Sozl0I+Wc9N%plCqO zY%fDy4eE900nA_dTK2|{r|~Z)xoLlRu7?phzQ938&*<;!%HZ3wsh{)!6S7)%J3>6Y zj#o=2lh5h0ORGR2c7&(O=fIAp;VL69shyBJa8Q$#mcw*w%HOd@2*~S-ODZ@&!um3} zmsW@D=i6e9>m5ZnJ93qpZEUQ-OXIs*xgz50#%@qej94G{ggDCSBOq()mffwrbOjt3 zyD?3W4cWQH0wEQD8gQbE?33nlqJVsQu>2e5Y+3~)&xPaf&;IKSb6sNh8(tV#tzteq zyHV*duzzs4F%0>e8)K{x)Q3`{TWhy|j14KuWa!2TU+)Ncf`p;MH_ow4CJQ{&FxUoP zk|V5{Nu@2g@--SDlN!lYT9Z{;^Y-;>p4>TL(#nVUljQL)qDma6_lU`fK-V8J|2!^Z zQc5y3I+10t$Y6xqrXN^9({{IrB;hX(cFD^Im94*$;7K3?*8mPKwqE%@*#|6(LdMmY z+hXV2t#NEM2E;%2D`QLoFN$nH&*FR7!JCt+ZegdO08-=h2wpqB&&D-oy*}(iOh$s2 z0(uJ2)@~O`3t_=BblIH*^B>?p=VSotS{4cQL)t-W!%p^ttw~o1ne#m)6f>#y(LxGv z%PSLy#q9U2XatZ8+089Z*F02nO3xj?s4>yrm}+Df5b*wU zyaGu23eSFtfjkzhiOG7;rJi)qnBQf0A)l|Z2g>1-@ocW`60pMYJGp&0l`CR^Fc{f|y*hB-D?tCg8 z6H>5=si`m5N}4aqp9;{SGwK%2fs$ac$B-jn|6gXHz8e4%q-g~$*?nBG#aF^}Wm zr(Vh$<|r(g#5T&M~4+MzF}OxJ@WhBgL96HJjfvcu@`+$*H2U7As5StIohzG zykHa*$UaE;-fQ3qVlI6!v%I|3j!*S#3-aDwmoD+hA^oO!Wy{UWX8+hM^hO{IXuW4< z=#0OI(7I5KG#ZR>&c6VM?SiEF+zPi1z5GpdXamu5cy9JX)eHUjGneI={r#AC1=_Au zeS9&7WG0&?RBdpO`S$lO)IhPfxf|57BjBy662moT|Mr100Y3Jf!=YcGKs2m#^M)IM zf}%>lD599p{&0608`5T1fw5@j#J=_NZF?*^=fps_EBFF~#0L&8&W@m>lL*E1JD$aC zS4n#h_o;CU)HaZs*AaAi(R}Gs2=NXu?BEOmRaxJutq1JavT@Xxi>Xeq4STGxcb8&m z7sw0@0+BRu@N3Bpf6ke{^iQoX;;)_-;gKD^G34j^*!Eh4`^ zU8~;6&rhKsJL}^nzq&<@Xn8*OPaaq_kk|QCSxSvJFmM|5-t_nHe>KVk^1?p4^p3hQvK+XW9$lm_`bL-9siG~IT==JLq z8_KKn?{9CvzVE#JS|Uuf9`euX7^7~r#l2!74_Kdhs9-zgFp@B+2}v#c|6->3oNjqF z0Wb$pGZ_!;yLzg6?K=Wbxuj2wXT8(nCPUY#W=@{^rPePmht1ll0qlnw` z>t1VsBt;az&1q(t=ZCt7ky(hDmHyevACm~Y+t*Oo53k{M&AyM^iy&lHzuTv33fFX} zd}lZgnS#8$Gk+Qk@-`;xZBna?3<>n&KW#Oo3fOE;gka!CWqBR0k5*R36c+X@_NF8! zHk^IIAiez28TKvo#p%fjNlbKdv*|2*^W`C+Y2N%c135I;J(RvQfq9o&P_dcrl|l!% z@+ycOq|Z2QKR3rJd;yDn{kMDd3MB|QRsK^o)1X?4B4zbEKYnRce^dybl)b#n1Qs1b zo!b5V%7BYzh*`E<;i4zJBYeK*)Ghal$~fQc{Owzym&;!Mu_W${`i>hiWIkH%t_XC5 zSF1x4j=%^gk=~hu=(@}M7xyF|Yr1IyHNl`=jPP;aL*w|Iub(Bo6Zx4#D&p^Z z@`9n1^z`s8t3p)N&btEXeUO_+#;ou5Jxf?&q>`1xax z>edDaQ`4EVtwu`ZxgjTnwkCDunM&>M+GnhIJ&z<>ad@G9Hf{DwxU={lT?-1sKTt?ZvXfpwv= z8<)S@biXXSz1nencs9L;qYV;&0H#n}lPTxdX%0COpsdt1y3y{ssBKmD7(uIWv)vgY zRG>H(^jdy>cP%OlKn)GxNuw6k)*;=C9vM13x!(WB=R6{Mq-_((@hucahWV8IXs9o zVV6ZvK9fenCJC6RA||_&T`Po(!U7cI^^?lP@%;!{_t%rV2b6p_2v3;qadXoPgsc8J z+M5E$qep#4rQzbgF`Ofl-;9X_2n6};sOw^#;U?g0AL^H@$|?*e znGh^_e>1N_pdgr{<_^|I#JsK=1@Y~_R=fjWSczVJwf9~w*2ixq_~b$^bKlS~p(phX zw_`(-^99=ZMSq6Sbb(DA7s>oUJQrM&^UwevDbXRb__5cJnVHELB_50ff=ANp=XC~0 z#k_0U?pB>fb#4ar9{zjQy@ijuN4b|E$a1jUDtfBXH}X+HcUyY4Y%H5D$D_=0X{_1% z4dy=;j7DrOjzwUkafCm~^w6#5WX zm-X80ob!$z^&`g9UssOUjKs18S@awJwe=d9#L?)K>Fenv3VW=hBP?sizagTvjt|Gf z`5dRxgqHFEHI*;5q*CvHb`kSMgQ3hn_M2DN&6Y4^!O&b!A{;;|Z{McVb?%xZM(CFR zBMT!!{BalI;`)x4`j3B#121eR)cp@*yK{sx3dEAV)*;O$M!;U?yckyjD{RZ2Avifq zoUSPPT?x^9gnj(1QE%Qw=TCFhQn0nN%g;-?{?Bai=DsKo3o#Qk!DfO{(*Ju0G= zBM8A6xzfU8LP$UW1enoe$9g3kLc#$az&vieOYD8SdA~P<`zUYJ6}%lL5ECpc%)(0# zn_k%bS6~K#hD$tn8@8c(L$)xE7jHglAUN>uAd>G0?d3Aig#j_wyGdaP2axz5h?Ua? zMNC^h@Y;_g6=_B-K3GKk<%_Kx;rJ;hCp-)~?0t{6$kk?kywp%mN>vB?`(63?d#%qZ z6k*lDB%pQ^Fd{b_*p1(FR#m`;^T1dYIK$m8PTljm-(_ULL=?Zv2OLUQQd&Wb6v=z?DOZFh4WEU^+q-zMSz*L>LlbhuWUC>QX(%Q#dcx*@B)|i#a z!}n8z4@>gHL)qxW-fG7)vBN&Wp8PrgnXR;@sz%)IM=WJ5lasXROrNnxAAgSgn?OtZ z!43BY{bL-)JBpr5e`-tb)F}6zU-EZb>Afzw;9lH2K5l7T0xn9D(B5I{{o9$8Yk(yDra|@aR1TTc#X85gSPe|V6zyM zQ&M!} zrDS#lepdo}a?%O`){gICavs2$98w05=EO1&jT^iGN;Dx*&MMAi(lk+QRByxvEnqr9 zPRegz4IQIpP|@)u;S|PM=8$6tDScBPIIduMHfjz45~$k0Qg0K<9*iBY#-GvcLq!_n z?^wsta(?{y@#zyF>J$ks!O0W$x-VT22X(3I@R6-rAV_j=uiOs*^ehZ%%BYn;2wWQm zrIfK!vru4{9Eti+GS3`zrFC?a#~^oo8l~y6=tc`_97YQ1dFtJH>E|VL2Ud8{d=bIz z_cDdO^JRfOVz1?4<+~A(=xCHV+29Q+ge@W5-LB_9cA)dl#l;1J(^aykaj{Xx&1S8W zLPyZ(y=;kEB*r-(cL^eD<;Y^ZO8|W6?%i_?T%gwfZal+n2^z8^K{sm&;>*sus|7TS z-USy-^ka*d+YiKamn7DI`7bd@cCcg{s$yA_CqA87THDfjp_%SYqYB$MDkqb0bR6m)7NyoU%Oh~?qKofg0-D^Q$)C(6K~#a@#F-P$IdXW zgRqK+4E zsD-a1_S1dv6+9`;%?DQlqaN2LnYQ7BBX4PYG>EAHeNez-lh!JVih+Y?pS~V=JTvGv-U3As_~Jl%n)1kYceh)_J40@JCYl57#eI2x z-?{IVE8OK3t{WcAt)(C@edKE3t+dp(>Nfk{3*bV0 zXSrc5@U4(pwg}>`esfYu-D-yu@Ow0rA1%IuN_ZOBv1HdZbH^vUq5SA(gL1RhT*$oE z)N*g@8Kvqbpu8+we`lq4_)KDP78R?(xMYQ9yU~$ihF7T--GX*r>?&mG#1pe|;Df{k zG|qA+#|=l@C?lyxussl=RojMw_S)=*R@G}o3bNl|MEa--P|E1&+(`dQMn>jKLk zj0YeumWYIMc4}_Nr*UyHh!d2!O)j{`C54O-HYe|`DSHydF5abfN^&STUdCCkX4WB7xVbyDoz4BY=8tOIR5pdt_0r5D5fZFfeGIUI8 z!NtfJc{XNvxH(OOM7~zdt<;-myno;FTS#YqKI~IiC}Y+BXW_V=Ta7|k2x7g%uP}r( zd83sy-dg^o^%TD??#ldR_ucg~S1qmgrPo7-JF|dSgak0)FC9DM4>!g##4^(oD#1RW zpOvmbJhbnHl;lz3MzhCnEj=@!7%ZuAp0^Hhew+Ks8#nLwF%8awSPmi~lD*7&W6bwx zlm31J7oP0lQy3vcv#Uv#V@K)ic{KSDMgJzFK9C9=5XkY->H)<)OPL~ zOorBX3YWL~&RiKn^})xUHx_cRkVMIZt))N|8mxW8fQ0(}eTC~~^eYn9KMUPS1tVXc za7eHSdS3d82blnQ()}Vh`Gpy|%y1X|gTAZ9f{;Y&(Y+6O9&)piU5@KU3c1!k>;zNYeZg=c2$1_xY)iFGE?T3B4XiZ9m?&>ZxZYxKm z6iYW39vT+jBqIkF;>L?i6F9wql<}MRWw3|oEOfz7UJX5-onl*d-T;&14t7^~$(6}z zXSU8=ba7fPCOB{4c9K~r%AOcJ>tj*b{Tclzbwe#3^OnP2MZ;i$Z;hi7^oru+Jpu$g zjUPYW#+|H34fus*y_ycqilMv;hb&}%Tf-m|TEyz5Iib`@aL~`Y0Fs0z%;=i>dFZpC znZcC->bQ1ILRhA|ovr5QOJI%eb15HdH1399x++p*YyI7=8 zFDOW{rdlQ${(1T7fVyd!F3^>zZ{C!|!X_Xf#R#5ND@bHr@;g~bb;6(VmKZs>P9(Rh z`kb%*p_;vHd_#$XFE%o=g+c<$EEF69JkQ2>7#V*RMomrJ85)#(MdM7Rm=jT-7;#xyoIf@Ja z{x$3@jyuK)F>&R$M^6R^LuhM;u=i>xp%{=cx?KfLyiXS38DTtX+~i{Qd+w)Lp&p#m zVYN9itSOF&>ZVUz(Cu5#?%DH$20>PKHiWhQd#F+GwyOD2T~BMsvgFWhsgJ4~#Oiie zmuLtiIXVCHnV>}0%nFOyqr`0R2%zM?7t`c39`#_-^qoCf!lv`6XXW5%5sn9f<$d|Pi?zArK+7~dnpeU4 zc?zDY4viHVam}_8+pH#^F^jgM(3IqDkM~e}m-UQEv`tbZSm-b{O52pusbh zaom}sad9b6<$I>U!5BqhAe?I+j?=AE=rCMp`?Tx>ZXVmS(Yhl4`s(0s1Sa3k7%VSD zt5=l)2h8UPPlnTK?G?tA<^s)wK9)hoyEyxWwVmeqZnVE`xQ@5EIyD4lBy$MQ?M~*;dHf^rubN zcxJXb@|Dh>1>g>A?_LbW5OHaQadUG+*=0EzGhv0>S3T2?OY*>+Jl8asU{7H$Q)7tf zok9{5uk+mfyn&Uat-nprUbN5e938P2B9Jru9({!+>E7N>`&&8 zQ$%}tgxQ2rtwFKm?L@0dM*(=5k57^#m!BhaYkVy1^i%H3!-LAeNJ=g2^>=&r-dLr* z|Hf*SQT^fH4T2a>a?=_ExSyI}s>0pQo={+TqoDNp>>nVHUB{<|C(z)8@3yRdTrK;} zDfPl{-&9eYP~QqpOh!(Vby`28NZ<;9)OH|3qoQ7r)Ew@1vnSuMmW;eFFNGNxgRRy$ z_CaC`DcN(ETPBM5HutGUMe^ystp*(1RCP<0}o2=-E#f8v$^t*tq@yjyhzJUR+#Q zB^?fGKs7e1&Ig3(C=%Q&haSdO4oy9@Ain8!2oOL+w9GCY^|jRWXNcr|R#^Fy6N-vP zQlk$L(w%wHdbgAE_KlaZFJq&k26@xxzXtf90hdacegWp;rtn~|3>~X%r%cf%9<=D6 zTyt+{Ls~L~0$*DF1-$>QivS(cAe(v*yYXswYi&|gECPaRU9diDl}vqxnnH=fKE-it zJu{##+V&NE@qx`vDpgN2;$8SaSL?Q-PSs9?h85fr6m&H0djyMXL~#&7av8*_txhIEe6F6E#rw=jW3}WBJba z2F$nlU#{Bwqn=+7QVZ_)45WkPp4pe@$@9lgRkx-ZMX$^y^Vk}KGZn!fLy1BukP;n+NPij4LxJHt&eKQ~-g`q;M}t~g&`CFpMN?ths4P$PF;tW=D>KRL~B z=i=ywj4a8)SCSa{iyQ7RSr)Y$eDA&%a35VcLwM+mMSE^9MK^VYtuVLWUsdj&MG3V) z^txgj8=KS&`xwq{>`<{w)9qig>>h!&Pe#>i9H-=!6n#B^wQQTqG*BM|-1z?XyI00G zaa>k@%J@bGm;2bdfJur=MSi~2jX=YC33sJ|@`?&AEi}>wWkAoy=I4TW<`hz*hw`;I zhSsW7R(XU#Kz)v*}Sg6XYciS8|;??@?>go#f z{-y#%aQ8-EZ|?!?V{Prr5JXV-!fuywrmktMgX}y%Rji6!0N1m&!jh)bHoK~Z-Cach z0s#$=ZQ@}JGT7p76v>%+=b!BM*mjaQrKabA!u<*gX}fZipg9O|y?a+SK+xyn6#t$q z-X5VFXj*p0otD7S=qgM@N(yeH3RjmPG?n6s2Jc&=%IWe8N)sK#33f1 zf-oy|0;jwD=l9m$6W@ubl>^pemPM>4wijWqL8eHbz-pCmnUd?A#@HQ|&nVnKQm97K z;%A8u0>6-xhtqbcFFVAFMl0Rd4%5=o@-jESb1akH$W>=d5PO2vZoEr*I(;|zw?=VZ zvk?_e3Wr|oztuyD>8M`ccwwi2l06e7)_wW!FK^{lZ~dj~R;bF?O6743+Z*s8qWfV^ z(w9+N>fNoKG(bW|HeSAbS2}NMIHh85DvtFE^<96IZq(%(le$5;OnLWn4VWljg z$O<52TeQcyvKSQkRNXjOvj`~*4rF_tIsX^v3_#a~m6KQSfx-x$WQ!dMXx9Vun?+$| zGc^?ltRB?8$79&7G-L@sogM7#PJ_`fv5*d?;z5Yp5o&OWnyblF{HgHVoX~>K#Pa*c zM%+E?xUm*SY&9mO8xx)Xj2q8lFC5%1Zd8%zO$W|E@Wv}jUM{@QTcdB=l_#5A^YjLC zh5K(k(Tx>>CxPISd2z!RMv9qghw9H=) zUeA0kD-s0`2Lt$xFm;5ScvRor<1cV{6C27B=-(@6p^P9RUtT)M$7_xB*bJ4B+L~+> za@wRqo0lZCFN3l6)L6Y!pt6UDo0}WJU16i>K{G}k8`Nx3iMqX?|C*O!_sxYR^gYxH z>4N^bk9BpUNqfQTbc-Ab!#OLdR_Je7SG>HtvQthd7oStG{?!h6m;j-f_G) z)^T7!#w#W%VhrS-+z=Mid+mLL{Af~Dynv;r&8|MV&7PBSYgT%je?K;sESy}W(LF%l zHL;=grEzsqQS1hwCS($P-L}SRS}!!2zB~n@nPkg(l}&DH|Zsl_i5v$SWS$HsPQLJ~zLyE!?e#!(O5%Hps`R zm-x(MW!#AjW;~~_F8!G`88cIpgr1GXT+{zjg&06hG~gDkgg`Gh;C@l1>op_cymCUH z+QXiX`Rm)-ZzO7G8yZT9ffN=NO7uHSQp=>n<&gIUMo%qAzm&10rly`9wp^0~OB&AO zTW9$}0QD7cqh6OMO&7i*6bs>;iPCRs*%`h#wl2MR`1eT1d2O*4E|?Zn#Ai0C((<5h z8}ZKwR$dH=L9Po_xs9OD+I+WA>)I^n@~ZI7!m zYN8x(1+1Vt`IukDG^U%q7xVi!{pgw6KO)>98bQv7#9e`kD2&7od!L_g`Htw=>>b4{ za%UkCi-hI-=ig7n4kz1JIWP)%$^6Ppsf5pMJ4eIUrJDY@H@!qbbaN@^x%MUP9d1tA zROnmD;N#(W?e#+PO)+gZMeF+v^KFc^)Y$nDJ(NY}CPQ3}<3kahZ)>=$kphi!CHKR& zQ9YMuX7wtT{w)Y%sBL>S!hCvp&xc)tm}brW;c;o4(R1-g|6|!)tL}3)WGMNM&-uFt ze0w#HqYw0;iNZ>%I4#<^HdHWge_tBw+sV&29oeBJW~tQ=9t!_^Bry)gr5-jSk_qwOYruoL z0?`kTFeo)Jg07$G`lrExuTL^RLecjBKR~^DHKAB6u0x-X_}>GrU(uRB@!~>8?kNfF z^=~j7G1DWK-$<(byY$8SO`&@IG$+h60gDfd zs{$pr`hR;%>>$c}PDM#VV$|RjV}_Jbb0FwUUV~^ua}VX}7TmHjK;OJciEvkv!@D?v zqp8h}z9AwL@lA#nn5PXqyZ&eiCi=UXW&wc{SJTQU9N~)$9lck3A=`umdr?-rpZY~?^fL4jRz?O=>q z=B`w=;}g(()q|qLyOA2M`H&Fsv~0uq1LFoH^|YPmxg*fggUl|@*uR%!axMqo5EsXK ziH-=J-@agtzJ6z73`h6EfFpGoCbi)75jt&gNG`lqj%|w^xd=1AkOLZDYtxw7qXTs| zM$X8r*YDmb1)*bNJd8RWiToa6wk^#5*dai+1|1YZwbGc4*_w5@HrZjXoPN(qrFYW`}w&B->z-aa*oghj0?p67{mv;j4(HG6tsaEn1{s`+YW zPDb$t-g=&$;8~_;I8of?`wxMM;b%g^2c+t7J6)0WTy05obHo&{T}&^VG^hXkZ;X7BK0v{rlCdz3-0GWD`ABnr-#f#h>Q!jj8&jmRYIxfWRLFbYTncV|ro$ z!~^NfHkQ_EeD5hp{Fz4?5Iz~T_jiV(p$5AD4QBT0sd5ujG>$8!%V z*xeqW*X~)?Xb`aL|7-Nb)GAVk0p;<#Bon)#CdhB}EME8MF56LaBJ^S|e7w9V;;X)8 z9f_(Eh;j5bh~|>;^DmX_Md9k4RWb<9F6qikxxJ~-gng~cCUGcQN(1qe=@~UHLkf8V zw&b#Ozo$9y#9L_F$l{D2$F`HF2Cp2^FP^vAl3Qfh0I=s`zd}V$POikHX3DOLH0o(P zx!CD9$*q*1;yq#=#@Bit+$JLg0!SaKmaojnT~YDgib?&T4Z)2&oY8}0W)mhS9@@o| zJU)%WB^k$#UmrDcm8u@AC-oUYBjXjPsPE=_n-Zl-z`+7odHAMRmz*D3D`Yu6Xu>`l z&Yziozvsu}*HII1ZA2XQ8fN9RSSO)KFf7^7NB>k;ooB3!L2TP#K2T$9HfY9Zc@6{A zSPT2X&l13!k2u*Ho>zlRhtF#F8jVYuo16Jm4ldpv{$qmm2)G>(#`g27{GNd4%5dU6%;v)mK$ZRP~4)V_thNu9o_w+cgy+?h;vrp zlsC4qvhS-m(DuYa?-0lYASp;|@vHfFc7b_x;8W!p)I9y8^X?@lH`*>^4eBRjipi0gHn?Kfd$-6o2EXRy|tKmYq z+oRWL)Y!yNInQP+Q^eB)GJ%(zXU%9fAK5eljtXe}iy+v1xbS6`T_qL&f)mR*8VAyt#c-gqTc>ys zvMXi?IhqZ>weGCt4zMsV9GzBOlMf{%zG-4Wv2|VE&AgO}eOX-9LSw zlvQeUTzSJ@TN1_hn~YU@Ys8WH{##ru)~j!;{Kg&08M!m-yVZZ<65F~N85QeNYY31O z&}46su^MXDxyhHvl?i?V19|g)SNB6tjjjg?@9qQBUc|L2jeE1g+5hx#nAqYhb_4ts z@r<>G#Y52fOBpNkZ;z*sR@9z7eyP#ua|EdmmY^tMjklVN6YIOM+pK)3A>_I8K54#e zciC_E#FqQPbglo=3tln5X7iVmt2%6DROWA>8^q}vL5tOByaD1bz2K9bJenjH10dXd zeKuY_{;-B#qR#znxCXZ=efxy0q~S1LNX435Hvagxh3J{z+){r2&$b^9Dq~HXdG2Ua zR2=SOgV4-B2q>zNh>=rFpqlnPhdc|bu7s7pAG247dzsY8En>2ZZPMG?W+e>@?I$PXM{GwhP1+uIcZvQ%Vl1%(z<|QB~I0ig0kd~YDUAVNQ zqP4}`ex)_p@^=wQ#;Uu!dA4P3X)P@)4fEhh&*@h*LQ{4XJ`EX}y^+ZGX%lIeP7P%) z2VI{#KL7gl3t$#~moA5xTLBzy%ce@4pKk@B29A{52YrP1?r|1>M<^aBU8ZIFF{oPp z)cEV)$PLcJCMk~$>jSbSLVez-K`hyCVXy0(9-)L9@aH~q{=jFv64cy8WtJ;93vb=v zB)5~f8T~YW#HI#aH!R4f>6!o-LR%KUK{9Grxb(D$YDJ3nH&K% zaN5#x&Ene6rp~s~o1X{l{^&e|5U+QYu0v7pvS^#QpQ`SjbdyG7p?k#=;$R86>_@V6 zkJqg8`z${#8SybA;g7||yZ*N?(`HaKJkwH|GtIs+n!WU8p5bV7O2l#-DbDe%Z6DSM zxEa^KN4!d9c#<-+w4EwqF+4Ttt4u8f8ANelDvJmYKRG)QOZlmYg+khA!k_sj`Hk)) zP97fJk+O%+bc?b?TFk3n(#-~I*=s8>nsg;pVBZCjEBf25B!4mY-NAd9aV*Ey!H_{7 zD@J)XdO$gn_avj-X)7ar-1D;XFb_m0-X{G*KL6fZ5}^mz`|XS|WCmododN|A-l*?! z%eR*%PtN_mFF{|0NM@%jITjs@-Nizbvfg3g2!!>c^)~&nMMA*D&~J9U=x+-+gnDFT-keiiO_)-tls(xUYyx9w>N_3! zyZprI?STjy?F*3U;z@ZPkAwdS8yR7SiiQ>%f(l3O!gcsao*SbFV+AD#G`|>V2d5XE zY=76Sz((OT)l_ReNnvcM6g)BSEGH`)!0Tx|Z;d}cuNW2{o_r9EBh!W^?xE~)R=r`e zb(JDh4!BN`caCFQ-Beds-<+uJ-J^Qckv~)bAe9mw-vhrY*w#G%Ufl5CK6Ew8?C$FF z`JS~Pu*dODyMP2+{Bc0Y*6h9ZYKMs?i}B)Tsqv34zgjI-i=2!$2623&j$3+bmbt%r zrT_#YP^Wokj1c<+HcpQ4&c=A2r>@uMNwY6{CHmcY`ilz-MvWdMWi8R`rw40J6JLMl zx~Imy@JAX~ejc(8Y?2!Z@IQr-qp)apQjknSv2jzG`NFPX)v9vlqa0mLwbV?_V#!hY zTMVOj4#T5KSDA7T)w-rhpKx?@vNr(+mf!1Smt<|ku^QuXoR%IW^YnW+W?tH|x2lOAR{ zMmy>J#?Qi2sOv^F`o&1TDI+O$$wdg}D@inHQXHI-lhf)S{la1SIrM29iJq+2DbN>T zd&E}fg^uV@#SR)~|trV~rzh~|_H;!(OM8?ZquVGi2O)(xSWv(by5Iw>e2 zN57h7-&a77EM5b76G&lxHAWPEW@$~a3RFmn#tfNtig*=wSURS!79B0 zG)|15ybPZxHBZISQke9Hj(BVZ1uBvcM8?3S{0)3!1D zN=Zrr3o9)>nG!j3MRuf^s~Qk~tEK3HHsj>{PcoaHGy(s%Ct`ukKKS;vt_vXw?BBxX zbtShUh8ehm?^Sc%wx&vMhKhf~^ZBnP`;59Bc57sQM>Ajjqw-~!t53kOUXYo?-%(J$$Qgy0M6IkTHuAFT-P+%oHTPP2Stsi#fc z%Y@yeN)>|LU7lNc5tesLI#?pc8y6TxuFdMAs7eEtB|!C}BESTR(Z!FJ9jaCimCtiD z#Kb?!3>_VdKbpFtri6eNH#Eto$+AL!^V284M~;O*?aYuGpZ_wpmgerOU=h*JeqT|X z7p19j9Z}Yp(;-nJ#z^h89v0)|SwF0!S7W-VjsE65U=aPDm{{}c8Dw7fiR-C6#7oJP zQ;E8K?dI;!I_r)^-js&ugPL{{V&cbv0SV0|1T9>~Nn=$8NpL&_+Ey&(kwCYfYJC8H zCV}bLB{pZjxIph)FaC-J8Gq#5EMn1N_fPji- z=lM2j_QS}?hxtBunY7WIx`p3=U~5dBZ8t{XQ^b0^x;@c!(0#fLN-<-f1^S9NrO9YW zQhm$C{S}bGh`FB*g1H4;Ks+{6X{~YuSEZ{jW@b!xpV#_)%|tw{(oJVudau%6x$$dk zO?{Wp7tJCD6j9#%G)GAkrGd&Z?X>dofkAuKTH~JH&LQ_ZC_b8) z;#k213tfr8Lk5w{#4A7~nP(I0apPtG)KEe}=<>U-ua^?F-XQ><7e*>GS>Z*$vxARM z8*`(v0F+;uQ~oJDjyK84$@O`>x<1)fG^h2J;vvQH<^+rfc;m)HxA<(O#4Tk!YK_DS zvS>?nS(KA1EMCOY1e`auKRio(gZy6lMAa0@d!=!C$rv?Um12tlf(X` zU~=QNx}5N?^WIK<<#fduiSp=$CaiW!seJx5|LQ@Caf~qz-qJ6(7*x&$gzic{-dVM} z)vZ~q>D^t;It@yrfqq$-Ym-NC>sXMtk7_EpzjQa>=3)AO(*|SgTGh0(cctlU!lxR3+@DnYXO{)ER zZ02t~L`xMJ>QH^zy>Yo-;VdsNWMOU|flnE)TUz90Tdl(li@I#Qya!;Xh!3^V8&-Wh@Ic8T%c3dm zXyId{oHuhTP7r6ZsJHe|J})V&v1OHf)6>Luic;Lh%fH=z?03=YtoOajJ%FO zK06Ry8+m3}VP}+-m#N(U*pr_su2)DJYbC<5Oy1vk%c5ee$%rFxcWD4AS&ir2U6R{fS|i)QhAod`sG!iGzb@ zTx~vDtt5Xa9t~qLu%u+hoQIhuhcI=8axmQxrp}T4xeG^8@XT~)IVIAf@Dsg8h+US# zDt!v~15qm-G;uG90I4IbH3Wi%GxdII>KfnmuRHcSR|#+4arzxcf>kY%NecuUL@!@z zp1x>5VD{??{O{{nMd47>(o!S&nyGW-25AZ`Mv&Mybu!-WpLx-Q$A9ipg@U?WYlJA()?5WDTDneCN_$i(@(nA5d#t&9mvl;6G^Lz6`!;3gaF{F9@gw0q0ED`l8UdLXeY_ z$ifdiQ!qOp+c^9^FRS(W{c_(sm~pU{tE|J}Z4Z=4P^O zRc7{vr8RLi$#l$hA}tY&Y@d#3v$|8o1U|DC~hrPrAI{A$QaSS1yL)@WLo)6*-=Arnna(W%NqYsT zk9+A8F>D~oom<7h>(EjDC!&?isd!+uh+s(AGQ-$YMhIB-RKs}X5w|hqVl-Ta^sRqn z(dz`r(7}KeoRH`De-sUpvxyD5DnAc^t9PrEcHwS!391wAU;aDz>)z0M;|13Lr#d*! z)GMKDA6N~lrAXOh^D;8=&vnlPSU@oL0N36ll6T%~(n?IY=#K~~^F1V*_=??Q)$INx zEcCYDF6z&Us|;vqw(h)|34HB_{h6efB+Z0I_g=Ohs2)!6dw_h)hEqJwmrwyl--G+xj43AGqxUN!*`tEW)$-!xp61k3i@q&Nnt98~5>67z&=~lk7 z23;p#o%=EyLY!%B{TeRVxo-SBBGvDwoxZuOw{R^O5~X|MzEI{jt)YpV(pX{V_9_OG zV=F}Ih_$9`lOvBnTR3?X+Gbzf*Y01|Le!I=JijW9gyDaPX`^ftLSp~DnyrQ2pui`o zXU$jSil^201mi;s|4D$&qnZL4-rCIQjl!3A1t@~-Gu*UR>;c`oLPT$RB)h+kknjH+;v45Zq^P^ z%3VfIAKe*^E+lM1GeqMe{+sd1P@6{^@EYgTJ?W5g+Ll9lZT|);3h;%0qrVO**_?_K zrbt;oGsO?+I5~e|#v;{|3&BQkA|p$_C+x*lYHG)9Zjh-%WY|Z-7-WZq=>xTeKJW=8cr0E@#oR`4?2fNPxN3$Yw+Fvj_GmmW=8b- zv1>v=00Qx|qi5G+|24y%O3k(?qlCj*1m3oxoW}F5L-XzD{`NidM2S(Cwu7RLo~rKj z4V-4E={q`d9crG^KMTnE{BMf?c#F+sp80=!)(tVPdAxeYEIdd5(%{gkoeoUg+yRNl zSw_am9OD6FDR`j{#qcf)*o@kqot^+9;E67~sA#4AYNbVIT)f064>3Zzj!9)UL8^yG zuibe6V_g;`O>e1n^kkh6M(56CE=mUuI)OBTz<-Q!fh`9U4^ zUu}B+NU5k0UfBhY@Gw}k)+t^Uh*r2ky?l05r~hBa29G!%vu%5C&aI9=ZTtbRSMI;T zD;xp!XC3K+XiNXlh5Ij6|NqC%Wdv_J8Y=vsMe)YEJMQ$9hQaIqvvHPsfP<)~X8pg9 zn!1nAK3i6W_>B9%%{4s1ap- zEmY@1rAF@^lQj!uW?YFUi&ZpL#1qw@-Gu*o*Zy3bxQHo^hp>l%Tc;woQ0&5Mn&(A%CN zi++U)H0ERPM9*e&uq2}XO}}jrzk7~kDgY6d7oYdjHZF#0!o&n_Ki&L&ZH_b2 zcQwB3&u0`Cda-OcnmnB zw$K}JOT&6iEyHf{0s_zReL3T3@FmhH&^*4HWr6vNqe z`eoiVYG*A|w^j$R*VWR^)>w1*_<;Jux*-b_VS3V(p?qh$8TYE#SO>uMr;56*sdl98 zU)h9yBNxzM$skdTh~{zP5!_ixa%l;F$-y3f9kd-d0pQ(ZLb```*WZh|Ag4h_FOr;+ zqLcTiJ+b`$$OnqiO^~$oekMgBNMU$OfL!{WPk0T1$BmAHE09nVB+1MIGqK?5B`}8qTA^a_3+REaaVWN|BL-2@|7} z6Bl`f0I$4`#j2CX%_M<_(W}PUx4qn4LG9D0oVXi?qC!F)%iTln1wxxWt3FsjE9!rN zEyq{=>w4Na0YD%&SIdy}2PoT{-Z$YCTxjACdDq(bNABENe7Xo5CQ09YPi;$Wz{Eoz zaVbwNy3FU5HCPClnSm5htw!7{@WFGDT5AA$FEi@;;sdK6Oh^T?G-VYf72t=xeid`6 z;I6m+;9|cOu`OHtEd%bXu%Qph^Pje{u}u*n+qiWe+mi9-5dC6XW?-Q=CnB%mnlJ{b zshASmt(OtN0D~25uir2*5LZjo)}dW~Z4^#?aNr4}yNUiLIf z6kE>+O-dY322_nWVB4efIqXW(}W0$PIpq&8N!^Xb*s5KGn;L9ZP`xza%+rfuG328?CgrP`kZ!=Jm zMCNbZi6yUWZ_SYl$n_uWZhM*dY71p#WWJF~g+nEy$u7PY+(Yf#;g3is7f4pw>SW9NG_o47lnklgvM zW9&6Q(v$q`XNfrYVSs=4pw>~NZFI+Gh*rQFy^vOhBaJF=*UWBB2=kv=9U>fDn^2

`Bs=#lad9Q{(oTf4&m@iPnYRcJVdu{x| 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�CqL#GvgmUMRoYh*!R;`81UIXfg17K88cf17EB@ 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 A#sB~S literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/kmp-runtime-layers.svg b/docs/developer-guide/img/kmp-runtime-layers.svg new file mode 100644 index 00000000000..1b64b17ff55 --- /dev/null +++ b/docs/developer-guide/img/kmp-runtime-layers.svg @@ -0,0 +1,44 @@ + + + + How a composable becomes Codename One components + Only the UI layer is replaced. Composition, state and coroutines are the real libraries, + and every node they emit is a Codename One container that the port draws natively. + + Your composables + Scaffold { LazyColumn { items(sessions) { Card { ... } } } } + + + Compose runtime (unmodified) + Composer, slot table, snapshots, Recomposer, remember, LaunchedEffect + + + compose-compat (Codename One) + Modifier chains, Box/Row/Column/Lazy policies, Text, material3, animation + + + Codename One core + UiNode is a Container: Graphics, fonts, text fields, scrolling, accessibility + + + Port + iOS (UIKit + Metal), Android, JavaScript, desktop, watch, TV + + ComposeHost + runs the Recomposer + BroadcastFrameClock fed by + Form.registerAnimated + = one frame per CN1 animation tick + + Dispatchers.Main + the EDT, found through a + build-time service registry + + Measuring + Compose constraints, outermost + modifier first; CN1 layout only + copies the result out + + + + diff --git a/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java index ed9df7ea4e3..5d9bb3ff129 100644 --- a/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java +++ b/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java @@ -4860,6 +4860,15 @@ public void usesClassMethod(String cls, String method) { } } + // The legacy AddressBook natives compile only under + // INCLUDE_CONTACTS_USAGE, which the usage-description block above + // turns on (every define, before Xcode 9). The frameworks go with + // the define: linked unconditionally they were loaded by every + // application at launch, and called by none without it. + if (xcodeVersion < 9 || privacyUsageDescriptions.containsKey("NSContactsUsageDescription")) { + addLibs = appendFrameworks(addLibs, "AddressBookUI.framework", "AddressBook.framework"); + } + // LocalAuthentication is required only when the app actually uses // com.codename1.security.Biometrics / SecureStorage. The scanner // above sets usesBiometrics if any com/codename1/security/ class diff --git a/maven/build-engine/src/main/java/com/codename1/maven/GradleConversion.java b/maven/build-engine/src/main/java/com/codename1/maven/GradleConversion.java index 70338d573c7..1a31f14427d 100644 --- a/maven/build-engine/src/main/java/com/codename1/maven/GradleConversion.java +++ b/maven/build-engine/src/main/java/com/codename1/maven/GradleConversion.java @@ -423,6 +423,11 @@ public ProjectLayout convert(File sourceDir, File targetDir, String cn1Version) // nothing: the app would build, minus its Kotlin classes. addKotlinPlugin(new File(targetDir, "build.gradle.kts"), true); } + if (KmpProjectImporter.isKmpProject(KmpProjectImporter.kmpDir(targetDir))) { + // Compose sources compile only with the Compose compiler, and + // the plugin refuses the build without it rather than guess. + addComposePlugin(new File(targetDir, "build.gradle.kts")); + } } catch (IOException ex) { throw new BuildFailureException("Could not convert " + sourceDir + ": " + ex.getMessage(), ex); } @@ -1044,6 +1049,26 @@ static void addKotlinPlugin(File buildScript, boolean withVersion) throws IOExce Files.write(buildScript.toPath(), updated.getBytes(StandardCharsets.UTF_8)); } + /// Adds the Compose compiler plugin, at the Kotlin plugin's version (the two + /// are released together and must match), to the `plugins {}` block + /// [#addKotlinPlugin] wrote. + static void addComposePlugin(File buildScript) throws IOException { + String text = new String(Files.readAllBytes(buildScript.toPath()), StandardCharsets.UTF_8); + if (text.contains(KmpProjectImporter.COMPOSE_GRADLE_PLUGIN)) { + return; + } + int at = text.indexOf("kotlin(\"jvm\")"); + int eol = at < 0 ? -1 : text.indexOf('\n', at); + if (eol < 0) { + addKotlinPlugin(buildScript, true); + addComposePlugin(buildScript); + return; + } + String line = "\n id(\"" + KmpProjectImporter.COMPOSE_GRADLE_PLUGIN + "\") version \"" + KOTLIN_VERSION + "\""; + Files.write(buildScript.toPath(), (text.substring(0, eol) + line + text.substring(eol)) + .getBytes(StandardCharsets.UTF_8)); + } + /// The iOS app extensions and localized strings, from wherever the source /// build tool keeps them to the Gradle layout's own directories. private static void copyIosExtras(ProjectLayout from, ProjectLayout to) throws IOException { @@ -1077,7 +1102,9 @@ private static void copyPlatformResources(ProjectLayout from, ProjectLayout to) // Added by the plugin for a project with src/main/unity, at its own // version, together with the translator of that version: a // declaration carried over from the pom could name another one. - "codenameone-unity-compat")); + "codenameone-unity-compat", + // Likewise for a project with src/main/kmp. + "codenameone-compose-compat")); /// `build.gradle.kts` lines for the dependencies a module's pom declares /// itself, leaving out the framework, which the plugin adds. A dependency of diff --git a/maven/build-engine/src/main/java/com/codename1/maven/KmpProjectImporter.java b/maven/build-engine/src/main/java/com/codename1/maven/KmpProjectImporter.java new file mode 100644 index 00000000000..dd463b36f80 --- /dev/null +++ b/maven/build-engine/src/main/java/com/codename1/maven/KmpProjectImporter.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.maven; + +import com.codename1.builders.BuildException; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/// Imports a Kotlin Multiplatform module into a Codename One application. +/// +/// The module's `commonMain` and its JVM source set (`jvmMain`, else +/// `desktopMain`) are copied into `common/src/main/kmp`, which the +/// archetype's `compose` profile compiles with the Compose compiler. The +/// Android and iOS source sets are not copied: they call the platforms +/// directly, and Codename One supplies both platforms itself. +/// +/// The application's main class is rewritten to show the module's root +/// composable in a `ComposeForm`. The composable is the one named, or the +/// only `@Composable fun App()` the common sources declare. +public final class KmpProjectImporter { + /// What an import did. + public static final class Result { + private File module; + private int copiedFiles; + private String entry; + private File mainClass; + private File setAside; + private final List skippedSourceSets = new ArrayList(); + private final List skippedEntryPoints = new ArrayList(); + + /// The module whose sources were imported. + public File getModule() { + return module; + } + + /// How many source files were copied. + public int getCopiedFiles() { + return copiedFiles; + } + + /// The fully qualified root composable the application shows. + public String getEntry() { + return entry; + } + + /// The main class the import wrote. + public File getMainClass() { + return mainClass; + } + + /// The previous main class, renamed out of the build, or null. + public File getSetAside() { + return setAside; + } + + /// Platform source sets that were not imported. + public List getSkippedSourceSets() { + return skippedSourceSets; + } + + /// JVM sources left out because they start the desktop application. + public List getSkippedEntryPoints() { + return skippedEntryPoints; + } + } + + /// The artifact a project with Kotlin Multiplatform sources depends on. + public static final String COMPOSE_ARTIFACT = "codenameone-compose-compat"; + /// The Kotlin compiler plugin that makes a function composable, applied by + /// a Gradle build under this id. + public static final String COMPOSE_GRADLE_PLUGIN = "org.jetbrains.kotlin.plugin.compose"; + + /// `src/main/kmp` of a common module: where the shared sources are imported. + public static File kmpDir(File common) { + return new File(common, "src/main/kmp"); + } + + /// The Kotlin source roots under [#kmpDir] a build compiles. + public static File[] kotlinDirs(File kmpDir) { + return new File[] {new File(kmpDir, "commonMain/kotlin"), new File(kmpDir, "jvmMain/kotlin")}; + } + + /// Whether the module has imported Kotlin Multiplatform sources. + public static boolean isKmpProject(File kmpDir) { + return kmpDir.isDirectory(); + } + + private static final String[] JVM_SETS = {"jvmMain", "desktopMain"}; + private static final String[] SKIPPED_SETS = {"androidMain", "iosMain", "appleMain", "nativeMain", "jsMain", + "wasmJsMain"}; + private static final Pattern PACKAGE = Pattern.compile("^\\s*package\\s+([\\w.]+)", Pattern.MULTILINE); + private static final Pattern WINDOW = Pattern.compile("^\\s*import\\s+androidx\\.compose\\.ui\\.window\\.", + Pattern.MULTILINE); + private static final Pattern APP = Pattern.compile("@Composable\\s+(?:public\\s+)?fun\\s+App\\s*\\(\\s*\\)"); + + /// The directory of the module to import: `source` itself when it has a + /// `src/commonMain`, else its only child that has one, else `module`. + public static File moduleDir(File source, String module) throws BuildException { + if (module != null && module.length() > 0) { + File m = new File(source, module); + if (!new File(m, "src/commonMain").isDirectory()) { + throw new BuildException("Module " + module + " of " + source + " has no src/commonMain"); + } + return m; + } + if (new File(source, "src/commonMain").isDirectory()) { + return source; + } + List found = new ArrayList(); + File[] kids = source.listFiles(); + if (kids != null) { + for (File k : kids) { + if (new File(k, "src/commonMain").isDirectory()) { + found.add(k); + } + } + } + if (found.size() == 1) { + return found.get(0); + } + if (found.isEmpty()) { + throw new BuildException("No Kotlin Multiplatform module (a directory with src/commonMain) in " + source); + } + StringBuilder names = new StringBuilder(); + for (File f : found) { + names.append(' ').append(f.getName()); + } + throw new BuildException("Several modules have a src/commonMain; choose one with -Dcn1.kmp.module:" + names); + } + + /// Copies the module's sources into `common` and writes the main class + /// `packageName`.`mainName`. `entry` is the fully qualified root + /// composable, or null to find `App()`. + public Result importProject(File source, String module, File common, String entry, String packageName, + String mainName) throws BuildException { + if (packageName == null || mainName == null) { + throw new BuildException("codename1.packageName and codename1.mainName must be set in " + + "codenameone_settings.properties"); + } + Result r = new Result(); + r.module = moduleDir(source, module); + File kmp = kmpDir(common); + File commonSrc = new File(r.module, "src/commonMain/kotlin"); + if (!commonSrc.isDirectory()) { + throw new BuildException(r.module + " has no src/commonMain/kotlin"); + } + try { + r.copiedFiles += copyTree(commonSrc, new File(kmp, "commonMain/kotlin")); + for (String set : JVM_SETS) { + File d = new File(r.module, "src/" + set + "/kotlin"); + if (d.isDirectory()) { + r.copiedFiles += copyJvmTree(d, new File(kmp, "jvmMain/kotlin"), r); + break; + } + } + for (String set : SKIPPED_SETS) { + if (new File(r.module, "src/" + set).isDirectory()) { + r.skippedSourceSets.add(set); + } + } + r.entry = entry != null && entry.length() > 0 ? entry : findApp(commonSrc); + writeMain(common, packageName, mainName, r); + } catch (IOException e) { + throw new BuildException("Importing " + r.module + " failed: " + e.getMessage(), e); + } + return r; + } + + private static String findApp(File dir) throws IOException, BuildException { + List found = new ArrayList(); + collectApps(dir, found); + if (found.size() == 1) { + return found.get(0); + } + throw new BuildException((found.isEmpty() ? "No" : "More than one") + " @Composable fun App() in the common " + + "sources" + (found.isEmpty() ? "" : " (" + found + ")") + + "; name the root composable with -Dcn1.kmp.entry=com.example.App"); + } + + private static void collectApps(File dir, List out) throws IOException { + File[] kids = dir.listFiles(); + if (kids == null) { + return; + } + for (File k : kids) { + if (k.isDirectory()) { + collectApps(k, out); + } else if (k.getName().endsWith(".kt")) { + String text = new String(Files.readAllBytes(k.toPath()), StandardCharsets.UTF_8); + if (APP.matcher(text).find()) { + Matcher p = PACKAGE.matcher(text); + out.add(p.find() ? p.group(1) + ".App" : "App"); + } + } + } + } + + private static void writeMain(File common, String pkg, String name, Result r) throws IOException { + String path = pkg.replace('.', '/'); + File kotlin = new File(common, "src/main/kotlin/" + path + "/" + name + "Compose.kt"); + kotlin.getParentFile().mkdirs(); + write(kotlin, "package " + pkg + "\n\n" + + "import com.codename1.compose.ComposeForm\n\n" + + "/// Shows the imported application's root composable. Called from " + name + ".runApp.\n" + + "fun startCompose() {\n" + + " ComposeForm { " + r.entry + "() }.show()\n" + + "}\n"); + File java = new File(common, "src/main/java/" + path + "/" + name + ".java"); + String source = "package " + pkg + ";\n\n" + + "import com.codename1.system.Lifecycle;\n\n" + + "/// Starts the imported Compose Multiplatform application, whose root\n" + + "/// composable " + name + "Compose.kt shows.\n" + + "public class " + name + " extends Lifecycle {\n" + + " @Override\n" + + " public void runApp() {\n" + + " " + name + "ComposeKt.startCompose();\n" + + " }\n" + + "}\n"; + if (java.isFile()) { + String old = new String(Files.readAllBytes(java.toPath()), StandardCharsets.UTF_8); + if (!old.equals(source)) { + File aside = new File(java.getParentFile(), name + ".java.before-kmp-import"); + Files.move(java.toPath(), aside.toPath(), StandardCopyOption.REPLACE_EXISTING); + r.setAside = aside; + } + } + java.getParentFile().mkdirs(); + write(java, source); + r.mainClass = java; + } + + private static void write(File f, String text) throws IOException { + Files.write(f.toPath(), text.getBytes(StandardCharsets.UTF_8)); + } + + /// Like [#copyTree], minus the files that open a desktop window. A desktop + /// target's `main` is `application { Window { App() } }`, which is Compose + /// for Desktop's windowing API; here the generated main class starts the + /// application instead, and that API does not exist. + private static int copyJvmTree(File from, File to, Result r) throws IOException { + int n = 0; + File[] kids = from.listFiles(); + if (kids == null) { + return 0; + } + to.mkdirs(); + for (File k : kids) { + File dest = new File(to, k.getName()); + if (k.isDirectory()) { + n += copyJvmTree(k, dest, r); + } else if (k.getName().endsWith(".kt") && WINDOW.matcher(new String(Files.readAllBytes(k.toPath()), + StandardCharsets.UTF_8)).find()) { + r.skippedEntryPoints.add(k); + } else { + Files.copy(k.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING); + n++; + } + } + return n; + } + + private static int copyTree(File from, File to) throws IOException { + int n = 0; + File[] kids = from.listFiles(); + if (kids == null) { + return 0; + } + to.mkdirs(); + for (File k : kids) { + File dest = new File(to, k.getName()); + if (k.isDirectory()) { + n += copyTree(k, dest); + } else { + Files.copy(k.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING); + n++; + } + } + return n; + } +} diff --git a/maven/build-engine/src/main/java/com/codename1/maven/Relocation.java b/maven/build-engine/src/main/java/com/codename1/maven/Relocation.java index b45763156c9..d6bac8dcedc 100644 --- a/maven/build-engine/src/main/java/com/codename1/maven/Relocation.java +++ b/maven/build-engine/src/main/java/com/codename1/maven/Relocation.java @@ -128,6 +128,9 @@ String shim(String internalName) { /// The relocated name when `internalName` falls under this layer's bridge /// package or one of its prefixes, else null. String relocate(String internalName) { + if (isCompose(internalName)) { + return null; + } if (runtimePackage != null && internalName.startsWith(runtimePackage)) { return runtimeTarget + internalName.substring(runtimePackage.length()); } @@ -143,6 +146,9 @@ String relocate(String internalName) { /// part of the API this layer provides. Unlike [#relocate] this leaves the /// bridge package out: that one is the layer's own, never an API name. boolean owns(String internalName) { + if (isCompose(internalName)) { + return false; + } for (String p : prefixes) { if (internalName.startsWith(p)) { return true; @@ -155,13 +161,29 @@ boolean owns(String internalName) { /// this layer's packages (`javax.swing.`), in an import or anywhere else. boolean namedInSource(String source) { for (String p : prefixes) { - if (source.indexOf(p.replace('/', '.')) >= 0) { - return true; + String dotted = p.replace('/', '.'); + int at = source.indexOf(dotted); + while (at >= 0) { + if (!source.startsWith(COMPOSE_DOTTED, at)) { + return true; + } + at = source.indexOf(dotted, at + 1); } } return false; } + /// Compose's `androidx.compose` packages belong to the Compose layer, which + /// ships them under their own names: the Android layer's `androidx/` prefix + /// must neither relocate them nor take an application naming them for an + /// Android one. + static boolean isCompose(String internalName) { + return internalName.startsWith(COMPOSE); + } + + static final String COMPOSE = "androidx/compose/"; + private static final String COMPOSE_DOTTED = "androidx.compose."; + /// The name the application was compiled against, when `relocated` is one /// this layer produced from a prefix, else null. Build messages use it: /// a developer knows `javax.swing.JTable`, not where it ships. diff --git a/maven/build-engine/src/test/java/com/codename1/maven/KmpProjectImporterTest.java b/maven/build-engine/src/test/java/com/codename1/maven/KmpProjectImporterTest.java new file mode 100644 index 00000000000..9bdf79068e8 --- /dev/null +++ b/maven/build-engine/src/test/java/com/codename1/maven/KmpProjectImporterTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.maven; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class KmpProjectImporterTest { + @TempDir + Path tmp; + + private static void write(File f, String text) throws IOException { + f.getParentFile().mkdirs(); + Files.write(f.toPath(), text.getBytes(StandardCharsets.UTF_8)); + } + + private static String read(File f) throws IOException { + return new String(Files.readAllBytes(f.toPath()), StandardCharsets.UTF_8); + } + + @Test + void importsSharedSourcesAndLeavesOutTheDesktopWindow() throws Exception { + File source = tmp.resolve("kmp").toFile(); + File module = new File(source, "composeApp"); + write(new File(module, "src/commonMain/kotlin/demo/App.kt"), + "package demo\n\n@Composable\nfun App() {\n}\n"); + write(new File(module, "src/desktopMain/kotlin/demo/Main.kt"), + "package demo\n\nimport androidx.compose.ui.window.application\n\nfun main() = application { }\n"); + write(new File(module, "src/desktopMain/kotlin/demo/Platform.kt"), "package demo\n\nval name = \"jvm\"\n"); + write(new File(module, "src/iosMain/kotlin/demo/Ios.kt"), "package demo\n"); + File common = tmp.resolve("app/common").toFile(); + common.mkdirs(); + + KmpProjectImporter.Result r = new KmpProjectImporter().importProject(source, null, common, null, + "com.acme", "Main"); + + assertEquals(module, r.getModule()); + assertEquals("demo.App", r.getEntry()); + File kmp = KmpProjectImporter.kmpDir(common); + assertTrue(new File(kmp, "commonMain/kotlin/demo/App.kt").isFile()); + assertTrue(new File(kmp, "jvmMain/kotlin/demo/Platform.kt").isFile()); + assertFalse(new File(kmp, "jvmMain/kotlin/demo/Main.kt").exists(), "the desktop window was copied"); + assertEquals(1, r.getSkippedEntryPoints().size()); + assertTrue(r.getSkippedSourceSets().contains("iosMain")); + assertTrue(read(r.getMainClass()).contains("extends Lifecycle")); + } + + @Test + void conversionAppliesTheComposeCompilerAtTheKotlinVersion() throws Exception { + File script = tmp.resolve("build.gradle.kts").toFile(); + write(script, "// header\n\ndependencies {\n}\n"); + GradleConversion.addKotlinPlugin(script, true); + GradleConversion.addComposePlugin(script); + GradleConversion.addComposePlugin(script); + String text = read(script); + assertTrue(text.contains("plugins {\n kotlin(\"jvm\") version \"" + GradleConversion.KOTLIN_VERSION + + "\"\n id(\"org.jetbrains.kotlin.plugin.compose\") version \"" + + GradleConversion.KOTLIN_VERSION + "\"\n}"), text); + assertEquals(text.indexOf("plugin.compose"), text.lastIndexOf("plugin.compose"), "added twice"); + } +} diff --git a/maven/cn1app-archetype/src/main/resources/archetype-resources/common/pom.xml b/maven/cn1app-archetype/src/main/resources/archetype-resources/common/pom.xml index 58cb693c8e3..19380f4dfe6 100644 --- a/maven/cn1app-archetype/src/main/resources/archetype-resources/common/pom.xml +++ b/maven/cn1app-archetype/src/main/resources/archetype-resources/common/pom.xml @@ -143,6 +143,68 @@ + + + compose + + + ${basedir}/src/main/kmp + + + + + 11 + + 23.0.0 + + + + + google + https://maven.google.com + + + + + com.codenameone + codenameone-compose-compat + + + + + + + maven-compiler-plugin + + 11 + 11 + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-compose-compiler-plugin-embeddable + ${kotlin.version} + + + + + + ${project.basedir}/src/main/desktop/kotlin ${project.basedir}/src/main/desktop/java + + ${project.basedir}/src/main/kmp/commonMain/kotlin + ${project.basedir}/src/main/kmp/jvmMain/kotlin -no-reflect diff --git a/maven/cn1app-archetype/src/main/resources/archetype-resources/pom.xml b/maven/cn1app-archetype/src/main/resources/archetype-resources/pom.xml index f74281b64f4..68b2db3fe01 100644 --- a/maven/cn1app-archetype/src/main/resources/archetype-resources/pom.xml +++ b/maven/cn1app-archetype/src/main/resources/archetype-resources/pom.xml @@ -28,6 +28,9 @@ ${cn1PluginVersion} ${cn1Version} UTF-8 + + 13.0 1.8 11 @@ -83,6 +86,11 @@ ${cn1.version} provided + + com.codenameone + codenameone-compose-compat + ${cn1.version} + + 2.2.10 + 1.12.1 + + 1.10.2 + + + + + + google + https://maven.google.com + + + + + + com.codenameone + codenameone-core + provided + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + androidx.compose.runtime + runtime-desktop + ${compose.runtime.version} + + + + androidx.compose.runtime + runtime-annotation-jvm + ${compose.runtime.version} + + + androidx.collection + collection-jvm + 1.5.0 + + + androidx.annotation + annotation-jvm + 1.9.1 + + + + org.jetbrains + annotations + 23.0.0 + + + org.jetbrains.kotlinx + kotlinx-coroutines-core-jvm + ${coroutines.version} + + + junit + junit + test + + + com.codenameone + codenameone-compat-testing + ${project.version} + test + + + + + src/main/kotlin + src/test/kotlin + + + com.github.spotbugs + spotbugs-maven-plugin + 4.7.3.4 + + Max + Low + false + true + ${project.build.directory} + ${project.basedir}/spotbugs-exclude.xml + + + + spotbugs + verify + + spotbugs + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.4.0 + + + hero-app + generate-test-sources + + add-test-source + + + + ${project.basedir}/../../scripts/kmp-bench/app/shared/commonMain/kotlin + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + 11 + + -no-reflect + + + + + compile + + compile + + + + test-compile + + test-compile + + + + + + + org.jetbrains.kotlin + kotlin-compose-compiler-plugin-embeddable + ${kotlin.version} + + + + + + + + + jdk8-no-tests + + 1.8 + + + true + + + + diff --git a/maven/compose-compat/spotbugs-exclude.xml b/maven/compose-compat/spotbugs-exclude.xml new file mode 100644 index 00000000000..7308bd85905 --- /dev/null +++ b/maven/compose-compat/spotbugs-exclude.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/animation/AnimatedVisibility.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/animation/AnimatedVisibility.kt new file mode 100644 index 00000000000..d3349c78bad --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/animation/AnimatedVisibility.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.animation + +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha + +/// How content appears. Every transition fades; sizes and slides fade too. +class EnterTransition internal constructor(internal val millis: Int) { + operator fun plus(other: EnterTransition): EnterTransition = EnterTransition(Math.max(millis, other.millis)) +} + +class ExitTransition internal constructor(internal val millis: Int) { + operator fun plus(other: ExitTransition): ExitTransition = ExitTransition(Math.max(millis, other.millis)) +} + +fun fadeIn(animationSpec: AnimationSpec? = null): EnterTransition = EnterTransition(220) +fun fadeOut(animationSpec: AnimationSpec? = null): ExitTransition = ExitTransition(220) +fun expandVertically(): EnterTransition = EnterTransition(220) +fun shrinkVertically(): ExitTransition = ExitTransition(220) +fun slideInVertically(initialOffsetY: (Int) -> Int = { -it / 2 }): EnterTransition = EnterTransition(220) +fun slideOutVertically(targetOffsetY: (Int) -> Int = { -it / 2 }): ExitTransition = ExitTransition(220) +fun scaleIn(): EnterTransition = EnterTransition(220) +fun scaleOut(): ExitTransition = ExitTransition(220) + +@Composable +fun AnimatedVisibility( + visible: Boolean, + modifier: Modifier = Modifier, + enter: EnterTransition = fadeIn() + expandVertically(), + exit: ExitTransition = fadeOut() + shrinkVertically(), + label: String = "AnimatedVisibility", + content: @Composable () -> Unit +) { + val alpha by animateFloatAsState(if (visible) 1f else 0f, tween(if (visible) enter.millis else exit.millis)) + if (visible || alpha > 0f) { + Box(modifier.alpha(alpha)) { + content() + } + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/animation/core/Animation.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/animation/core/Animation.kt new file mode 100644 index 00000000000..77ce654cfda --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/animation/core/Animation.kt @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.animation.core + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.withFrameNanos +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp + +/// Maps a fraction of an animation's time to a fraction of its distance. +@Immutable +fun interface Easing { + fun transform(fraction: Float): Float +} + +/// A cubic bezier from (0, 0) to (1, 1) through two control points, as CSS +/// defines `cubic-bezier`. +@Immutable +class CubicBezierEasing(private val a: Float, private val b: Float, private val c: Float, private val d: Float) : Easing { + private fun bezier(t: Float, p1: Float, p2: Float): Float { + val u = 1 - t + return 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t + } + + override fun transform(fraction: Float): Float { + if (fraction <= 0f) return 0f + if (fraction >= 1f) return 1f + var lo = 0f + var hi = 1f + var t = fraction + for (i in 0 until 20) { + val x = bezier(t, a, c) + if (Math.abs(x - fraction) < 0.0005f) break + if (x < fraction) lo = t else hi = t + t = (lo + hi) / 2 + } + return bezier(t, b, d) + } +} + +val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f) +val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f) +val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f) +val LinearEasing: Easing = Easing { it } +val EaseInOut: Easing = CubicBezierEasing(0.42f, 0.0f, 0.58f, 1.0f) + +/// How a value moves to its target. +interface AnimationSpec + +/// Answers the value at a time for an animation of a float from `from` to `to`. +internal interface FloatSpec { + /// The value at `playTimeNanos`, or null once the animation is over. + fun valueAt(playTimeNanos: Long, from: Float, to: Float, velocity: FloatArray): Float? +} + +@Immutable +class TweenSpec(val durationMillis: Int = DefaultDurationMillis, val delay: Int = 0, val easing: Easing = FastOutSlowInEasing) : + AnimationSpec, FloatSpec { + override fun valueAt(playTimeNanos: Long, from: Float, to: Float, velocity: FloatArray): Float? { + val ms = playTimeNanos / 1_000_000L - delay + if (ms < 0) return from + if (ms >= durationMillis) return null + val f = easing.transform(ms.toFloat() / Math.max(1, durationMillis)) + return from + (to - from) * f + } +} + +/// A damped spring. Integrated per frame from the velocity it carries. +@Immutable +class SpringSpec(val dampingRatio: Float = Spring.DampingRatioNoBouncy, val stiffness: Float = Spring.StiffnessMedium) : + AnimationSpec, FloatSpec { + override fun valueAt(playTimeNanos: Long, from: Float, to: Float, velocity: FloatArray): Float? { + // velocity[0] = current value, velocity[1] = velocity, velocity[2] = last time. + if (velocity[2] < 0f) { + velocity[0] = from + velocity[1] = 0f + velocity[2] = 0f + } + val t = playTimeNanos / 1e9f + var dt = t - velocity[2] + velocity[2] = t + val omega = Math.sqrt(stiffness.toDouble()).toFloat() + var x = velocity[0] + var v = velocity[1] + while (dt > 0f) { + val step = Math.min(dt, 1f / 240f) + val a = -stiffness * (x - to) - 2f * dampingRatio * omega * v + v += a * step + x += v * step + dt -= step + } + velocity[0] = x + velocity[1] = v + val range = Math.max(Math.abs(to - from), 1f) + if (Math.abs(x - to) < range * 0.001f && Math.abs(v) < range * 0.01f) return null + return x + } +} + +object Spring { + const val DampingRatioHighBouncy: Float = 0.2f + const val DampingRatioMediumBouncy: Float = 0.5f + const val DampingRatioLowBouncy: Float = 0.75f + const val DampingRatioNoBouncy: Float = 1f + const val StiffnessHigh: Float = 10_000f + const val StiffnessMedium: Float = 1500f + const val StiffnessMediumLow: Float = 400f + const val StiffnessLow: Float = 200f + const val StiffnessVeryLow: Float = 50f +} + +const val DefaultDurationMillis: Int = 300 + +fun tween(durationMillis: Int = DefaultDurationMillis, delayMillis: Int = 0, easing: Easing = FastOutSlowInEasing): TweenSpec = + TweenSpec(durationMillis, delayMillis, easing) + +fun spring(dampingRatio: Float = Spring.DampingRatioNoBouncy, stiffness: Float = Spring.StiffnessMedium, + visibilityThreshold: T? = null): SpringSpec = SpringSpec(dampingRatio, stiffness) + +enum class RepeatMode { Restart, Reverse } + +@Immutable +class InfiniteRepeatableSpec(val animation: TweenSpec, val repeatMode: RepeatMode) : AnimationSpec + +fun infiniteRepeatable(animation: TweenSpec, repeatMode: RepeatMode = RepeatMode.Restart): InfiniteRepeatableSpec = + InfiniteRepeatableSpec(animation, repeatMode) + +/// A float that animates to targets on the frame clock. +class Animatable(initialValue: Float) { + private var current by mutableFloatStateOf(initialValue) + private var running by mutableStateOf(false) + var targetValue: Float = initialValue + private set + + val value: Float get() = current + val isRunning: Boolean get() = running + + suspend fun snapTo(targetValue: Float) { + this.targetValue = targetValue + current = targetValue + } + + suspend fun animateTo(targetValue: Float, animationSpec: AnimationSpec = spring()) { + this.targetValue = targetValue + val spec = (animationSpec as? FloatSpec) ?: SpringSpec() + val from = current + if (from == targetValue) return + val state = floatArrayOf(0f, 0f, -1f) + running = true + try { + var start = -1L + while (true) { + val done = withFrameNanos { now -> + if (start < 0) start = now + val v = spec.valueAt(now - start, from, targetValue, state) + if (v == null) { + current = targetValue + true + } else { + current = v + false + } + } + if (done) break + } + } finally { + running = false + } + } + + fun asState(): State = object : State { + override val value: Float get() = current + } +} + +@Composable +fun animateFloatAsState(targetValue: Float, animationSpec: AnimationSpec = spring(), label: String = "FloatAnimation", + finishedListener: ((Float) -> Unit)? = null): State { + val anim = remember { Animatable(targetValue) } + LaunchedEffect(targetValue) { + anim.animateTo(targetValue, animationSpec) + finishedListener?.invoke(targetValue) + } + return anim.asState() +} + +@Composable +fun animateDpAsState(targetValue: Dp, animationSpec: AnimationSpec = spring(), label: String = "DpAnimation"): State { + @Suppress("UNCHECKED_CAST") + val f = animateFloatAsState(targetValue.value, animationSpec as AnimationSpec, label) + return remember(f) { object : State { override val value: Dp get() = Dp(f.value) } } +} + +/// Runs animations that never end: the values a [rememberInfiniteTransition] +/// produces. +class InfiniteTransition internal constructor() { + internal var startNanos by mutableStateOf(-1L) + internal var nowNanos by mutableStateOf(0L) +} + +@Composable +fun rememberInfiniteTransition(label: String = "InfiniteTransition"): InfiniteTransition { + val t = remember { InfiniteTransition() } + LaunchedEffect(t) { + while (true) { + withFrameNanos { now -> + if (t.startNanos < 0) t.startNanos = now + t.nowNanos = now - t.startNanos + } + } + } + return t +} + +@Composable +fun InfiniteTransition.animateFloat(initialValue: Float, targetValue: Float, animationSpec: InfiniteRepeatableSpec, + label: String = "FloatAnimation"): State { + val t = this + return remember(initialValue, targetValue, animationSpec) { + object : State { + override val value: Float + get() { + val spec = animationSpec.animation + val period = (spec.durationMillis + spec.delay).toLong() * 1_000_000L + if (period <= 0) return targetValue + val cycle = t.nowNanos / period + val inCycle = t.nowNanos % period + val reverse = animationSpec.repeatMode == RepeatMode.Reverse && cycle % 2 == 1L + val v = spec.valueAt(inCycle, initialValue, targetValue, floatArrayOf(0f, 0f, -1f)) ?: targetValue + return if (reverse) initialValue + targetValue - v else v + } + } + } +} + +@Composable +fun animateColorAsState(targetValue: Color, animationSpec: AnimationSpec = spring(), label: String = "ColorAnimation"): State { + val from = remember { mutableStateOf(targetValue) } + val to = remember { mutableStateOf(targetValue) } + if (to.value != targetValue) { + from.value = to.value + to.value = targetValue + } + val anim = remember { Animatable(1f) } + LaunchedEffect(targetValue) { + anim.snapTo(0f) + anim.animateTo(1f, animationSpec) + } + return remember { + object : State { + override val value: Color get() = androidx.compose.ui.graphics.lerp(from.value, to.value, anim.value) + } + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/Foundation.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/Foundation.kt new file mode 100644 index 00000000000..0244a5e1bdf --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/Foundation.kt @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.foundation + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.unit.Dp +import com.codename1.compose.internal.BackgroundElement +import com.codename1.compose.internal.BorderElement +import com.codename1.compose.internal.ClickableElement +import com.codename1.compose.internal.DrawBehindElement +import com.codename1.compose.internal.ImageNode +import com.codename1.compose.internal.LeafNode +import com.codename1.compose.internal.LeafPolicy +import com.codename1.compose.internal.ScrollElement +import com.codename1.compose.internal.ScrollStateHandle +import com.codename1.compose.internal.UiNode +import androidx.compose.runtime.ComposeNode +import com.codename1.compose.internal.NodeApplier + +fun Modifier.background(color: Color, shape: Shape = RectangleShape): Modifier = + this.then(BackgroundElement(color, null, shape)) + +fun Modifier.background(brush: Brush, shape: Shape = RectangleShape, alpha: Float = 1f): Modifier = + this.then(BackgroundElement(Color.Unspecified, brush, shape)) + +fun Modifier.border(width: Dp, color: Color, shape: Shape = RectangleShape): Modifier = + this.then(BorderElement(width, color, shape)) + +fun Modifier.border(border: BorderStroke, shape: Shape = RectangleShape): Modifier = + this.then(BorderElement(border.width, border.color, shape)) + +class BorderStroke(val width: Dp, val color: Color) + +fun Modifier.clickable(enabled: Boolean = true, onClickLabel: String? = null, onClick: () -> Unit): Modifier = + this.then(ClickableElement(enabled, onClick, null, true)) + +fun Modifier.combinedClickable(enabled: Boolean = true, onLongClick: (() -> Unit)? = null, onClick: () -> Unit): Modifier = + this.then(ClickableElement(enabled, onClick, onLongClick, true)) + +/// The scroll position of a [verticalScroll] or [horizontalScroll] modifier, in +/// pixels. +@Stable +class ScrollState(initial: Int) { + private var node: UiNode? = null + private var vertical = true + private var stored by mutableIntStateOf(initial) + + val value: Int + get() { + val n = node ?: return stored + return if (vertical) n.scrollY else n.scrollX + } + + val maxValue: Int + get() { + val n = node ?: return 0 + return if (vertical) Math.max(0, n.scrollDimension.height - n.height) + else Math.max(0, n.scrollDimension.width - n.width) + } + + fun scrollTo(value: Int) { + stored = value + val n = node ?: return + if (vertical) n.cn1ScrollY(value) else n.cn1ScrollX(value) + } + + suspend fun animateScrollTo(value: Int) { + scrollTo(value) + } + + internal val handle: ScrollStateHandle = object : ScrollStateHandle { + override fun attach(node: UiNode, vertical: Boolean) { + this@ScrollState.node = node + this@ScrollState.vertical = vertical + if (stored != 0) { + node.cn1ScrollPending(stored, vertical) + } + } + + override fun detach(node: UiNode) { + if (this@ScrollState.node === node) this@ScrollState.node = null + } + } +} + +@Composable +fun rememberScrollState(initial: Int = 0): ScrollState = remember { ScrollState(initial) } + +fun Modifier.verticalScroll(state: ScrollState, enabled: Boolean = true): Modifier = + this.then(ScrollElement(state.handle, true)) + +fun Modifier.horizontalScroll(state: ScrollState, enabled: Boolean = true): Modifier = + this.then(ScrollElement(state.handle, false)) + +/// A box that draws with [onDraw]; its size comes from `modifier`. +@Composable +fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) { + LeafNode(modifier.then(DrawBehindElement(onDraw)), LeafPolicy) +} + +@Composable +fun Image( + bitmap: ImageBitmap, + contentDescription: String?, + modifier: Modifier = Modifier, + alignment: Alignment = Alignment.Center, + contentScale: androidx.compose.ui.layout.ContentScale = androidx.compose.ui.layout.ContentScale.Fit, + alpha: Float = 1f +) { + ComposeNode( + factory = { ImageNode() }, + update = { + set(modifier) { this.modifier = it } + set(bitmap) { this.bitmap = it } + set(contentScale) { this.contentScale = it } + } + ) +} + +@Composable +fun isSystemInDarkTheme(): Boolean = com.codename1.compose.internal.Theme.isDark() diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/layout/Arrangement.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/layout/Arrangement.kt new file mode 100644 index 00000000000..afbdbd355d9 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/layout/Arrangement.kt @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.foundation.layout + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.Alignment +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import com.codename1.compose.internal.Density + +/// How a Row or Column distributes its children along its main axis. +object Arrangement { + @Immutable + interface Horizontal { + val spacing: Dp get() = Dp(0f) + fun arrange(totalSize: Int, sizes: IntArray, layoutDirection: LayoutDirection, outPositions: IntArray) + } + + @Immutable + interface Vertical { + val spacing: Dp get() = Dp(0f) + fun arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) + } + + @Immutable + interface HorizontalOrVertical : Horizontal, Vertical { + override val spacing: Dp get() = Dp(0f) + } + + private class Kind(val name: String, val gap: (Int, Int) -> Pair) : HorizontalOrVertical { + // gap(free, count) answers (leading, between). + override fun arrange(totalSize: Int, sizes: IntArray, layoutDirection: LayoutDirection, outPositions: IntArray) = + arrange(totalSize, sizes, outPositions) + + override fun arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) { + var used = 0 + for (s in sizes) used += s + val (lead, between) = gap(Math.max(0, totalSize - used), sizes.size) + var p = lead + for (i in sizes.indices) { + outPositions[i] = p + p += sizes[i] + between + } + } + + override fun toString(): String = "Arrangement#$name" + } + + val Start: Horizontal = Kind("Start") { _, _ -> 0 to 0 } + val End: Horizontal = Kind("End") { f, _ -> f to 0 } + val Top: Vertical = Kind("Top") { _, _ -> 0 to 0 } + val Bottom: Vertical = Kind("Bottom") { f, _ -> f to 0 } + val Center: HorizontalOrVertical = Kind("Center") { f, _ -> f / 2 to 0 } + val SpaceBetween: HorizontalOrVertical = Kind("SpaceBetween") { f, n -> 0 to if (n > 1) f / (n - 1) else 0 } + val SpaceEvenly: HorizontalOrVertical = Kind("SpaceEvenly") { f, n -> f / (n + 1) to f / (n + 1) } + val SpaceAround: HorizontalOrVertical = Kind("SpaceAround") { f, n -> + if (n == 0) 0 to 0 else f / (2 * n) to f / n + } + + /// Places children `space` apart, from the start. + fun spacedBy(space: Dp): HorizontalOrVertical = SpacedAligned(space, null, null) + + fun spacedBy(space: Dp, alignment: Alignment.Horizontal): Horizontal = SpacedAligned(space, alignment, null) + + fun spacedBy(space: Dp, alignment: Alignment.Vertical): Vertical = SpacedAligned(space, null, alignment) + + private data class SpacedAligned(val space: Dp, val h: Alignment.Horizontal?, val v: Alignment.Vertical?) : + HorizontalOrVertical { + override val spacing: Dp get() = space + + override fun arrange(totalSize: Int, sizes: IntArray, layoutDirection: LayoutDirection, outPositions: IntArray) { + place(totalSize, sizes, outPositions) { used -> h?.align(used, totalSize, layoutDirection) ?: 0 } + } + + override fun arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) { + place(totalSize, sizes, outPositions) { used -> v?.align(used, totalSize) ?: 0 } + } + + private inline fun place(totalSize: Int, sizes: IntArray, out: IntArray, lead: (Int) -> Int) { + val gap = Density.px(space) + var used = 0 + for (s in sizes) used += s + if (sizes.size > 1) used += gap * (sizes.size - 1) + var p = lead(used) + for (i in sizes.indices) { + out[i] = p + p += sizes[i] + gap + } + } + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/layout/Layout.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/layout/Layout.kt new file mode 100644 index 00000000000..c775163725d --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/layout/Layout.kt @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.foundation.layout + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import com.codename1.compose.internal.AlignSelfElement +import com.codename1.compose.internal.AspectRatioElement +import com.codename1.compose.internal.BoxPolicy +import com.codename1.compose.internal.FillElement +import com.codename1.compose.internal.LayoutNode +import com.codename1.compose.internal.LeafNode +import com.codename1.compose.internal.LeafPolicy +import com.codename1.compose.internal.LinearPolicy +import com.codename1.compose.internal.MatchParentSizeElement +import com.codename1.compose.internal.OffsetElement +import com.codename1.compose.internal.PaddingElement +import com.codename1.compose.internal.SizeElement +import com.codename1.compose.internal.WeightElement +import com.codename1.compose.internal.WrapElement + +// --- containers ------------------------------------------------------------- + +@Stable +interface BoxScope { + fun Modifier.align(alignment: Alignment): Modifier = this.then(AlignSelfElement(null, null, alignment)) + fun Modifier.matchParentSize(): Modifier = this.then(MatchParentSizeElement) +} + +@Stable +interface RowScope { + fun Modifier.weight(weight: Float, fill: Boolean = true): Modifier = this.then(WeightElement(weight, fill)) + fun Modifier.align(alignment: Alignment.Vertical): Modifier = this.then(AlignSelfElement(null, alignment, null)) +} + +@Stable +interface ColumnScope { + fun Modifier.weight(weight: Float, fill: Boolean = true): Modifier = this.then(WeightElement(weight, fill)) + fun Modifier.align(alignment: Alignment.Horizontal): Modifier = this.then(AlignSelfElement(alignment, null, null)) +} + +internal object BoxScopeInstance : BoxScope +internal object RowScopeInstance : RowScope +internal object ColumnScopeInstance : ColumnScope + +@Composable +fun Box( + modifier: Modifier = Modifier, + contentAlignment: Alignment = Alignment.TopStart, + propagateMinConstraints: Boolean = false, + content: @Composable BoxScope.() -> Unit +) { + val policy = remember(contentAlignment, propagateMinConstraints) { BoxPolicy(contentAlignment, propagateMinConstraints) } + LayoutNode(modifier, policy) { BoxScopeInstance.content() } +} + +@Composable +fun Box(modifier: Modifier) { + LeafNode(modifier, BoxPolicy.TopStart) +} + +@Composable +fun Row( + modifier: Modifier = Modifier, + horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, + verticalAlignment: Alignment.Vertical = Alignment.Top, + content: @Composable RowScope.() -> Unit +) { + val policy = remember(horizontalArrangement, verticalAlignment) { + LinearPolicy(false, horizontalArrangement, null, null, verticalAlignment) + } + LayoutNode(modifier, policy) { RowScopeInstance.content() } +} + +@Composable +fun Column( + modifier: Modifier = Modifier, + verticalArrangement: Arrangement.Vertical = Arrangement.Top, + horizontalAlignment: Alignment.Horizontal = Alignment.Start, + content: @Composable ColumnScope.() -> Unit +) { + val policy = remember(verticalArrangement, horizontalAlignment) { + LinearPolicy(true, null, verticalArrangement, horizontalAlignment, null) + } + LayoutNode(modifier, policy) { ColumnScopeInstance.content() } +} + +@Composable +fun Spacer(modifier: Modifier) { + LeafNode(modifier, LeafPolicy) +} + +// --- padding ---------------------------------------------------------------- + +@Immutable +interface PaddingValues { + fun calculateLeftPadding(layoutDirection: androidx.compose.ui.unit.LayoutDirection): Dp + fun calculateTopPadding(): Dp + fun calculateRightPadding(layoutDirection: androidx.compose.ui.unit.LayoutDirection): Dp + fun calculateBottomPadding(): Dp +} + +@Immutable +private data class PaddingValuesImpl(val start: Dp, val top: Dp, val end: Dp, val bottom: Dp) : PaddingValues { + override fun calculateLeftPadding(layoutDirection: androidx.compose.ui.unit.LayoutDirection): Dp = start + override fun calculateTopPadding(): Dp = top + override fun calculateRightPadding(layoutDirection: androidx.compose.ui.unit.LayoutDirection): Dp = end + override fun calculateBottomPadding(): Dp = bottom +} + +fun PaddingValues(all: Dp): PaddingValues = PaddingValuesImpl(all, all, all, all) +fun PaddingValues(horizontal: Dp = Dp(0f), vertical: Dp = Dp(0f)): PaddingValues = + PaddingValuesImpl(horizontal, vertical, horizontal, vertical) +fun PaddingValues(start: Dp = Dp(0f), top: Dp = Dp(0f), end: Dp = Dp(0f), bottom: Dp = Dp(0f)): PaddingValues = + PaddingValuesImpl(start, top, end, bottom) + +fun Modifier.padding(all: Dp): Modifier = this.then(PaddingElement(all, all, all, all)) +fun Modifier.padding(horizontal: Dp = Dp(0f), vertical: Dp = Dp(0f)): Modifier = + this.then(PaddingElement(horizontal, vertical, horizontal, vertical)) +fun Modifier.padding(start: Dp = Dp(0f), top: Dp = Dp(0f), end: Dp = Dp(0f), bottom: Dp = Dp(0f)): Modifier = + this.then(PaddingElement(start, top, end, bottom)) +fun Modifier.padding(paddingValues: PaddingValues): Modifier = this.then(PaddingElement( + paddingValues.calculateLeftPadding(androidx.compose.ui.unit.LayoutDirection.Ltr), paddingValues.calculateTopPadding(), + paddingValues.calculateRightPadding(androidx.compose.ui.unit.LayoutDirection.Ltr), paddingValues.calculateBottomPadding())) + +// --- size ------------------------------------------------------------------- + +fun Modifier.size(size: Dp): Modifier = this.then(SizeElement(size, size, size, size)) +fun Modifier.size(width: Dp, height: Dp): Modifier = this.then(SizeElement(width, width, height, height)) +fun Modifier.width(width: Dp): Modifier = this.then(SizeElement(minWidth = width, maxWidth = width)) +fun Modifier.height(height: Dp): Modifier = this.then(SizeElement(minHeight = height, maxHeight = height)) +fun Modifier.requiredSize(size: Dp): Modifier = this.then(SizeElement(size, size, size, size, false)) +fun Modifier.requiredWidth(width: Dp): Modifier = this.then(SizeElement(minWidth = width, maxWidth = width, enforceIncoming = false)) +fun Modifier.requiredHeight(height: Dp): Modifier = this.then(SizeElement(minHeight = height, maxHeight = height, enforceIncoming = false)) +fun Modifier.sizeIn(minWidth: Dp = Dp.Unspecified, minHeight: Dp = Dp.Unspecified, maxWidth: Dp = Dp.Unspecified, + maxHeight: Dp = Dp.Unspecified): Modifier = this.then(SizeElement(minWidth, maxWidth, minHeight, maxHeight)) +fun Modifier.widthIn(min: Dp = Dp.Unspecified, max: Dp = Dp.Unspecified): Modifier = + this.then(SizeElement(minWidth = min, maxWidth = max)) +fun Modifier.heightIn(min: Dp = Dp.Unspecified, max: Dp = Dp.Unspecified): Modifier = + this.then(SizeElement(minHeight = min, maxHeight = max)) +fun Modifier.defaultMinSize(minWidth: Dp = Dp.Unspecified, minHeight: Dp = Dp.Unspecified): Modifier = + this.then(SizeElement(minWidth = minWidth, minHeight = minHeight)) + +fun Modifier.fillMaxWidth(fraction: Float = 1f): Modifier = this.then(FillElement(true, false, fraction)) +fun Modifier.fillMaxHeight(fraction: Float = 1f): Modifier = this.then(FillElement(false, true, fraction)) +fun Modifier.fillMaxSize(fraction: Float = 1f): Modifier = this.then(FillElement(true, true, fraction)) + +fun Modifier.wrapContentWidth(align: Alignment.Horizontal = Alignment.CenterHorizontally, unbounded: Boolean = false): Modifier = + this.then(WrapElement(true, false, Alignment { s, sp, d -> androidx.compose.ui.unit.IntOffset(align.align(s.width, sp.width, d), 0) }, unbounded)) +fun Modifier.wrapContentHeight(align: Alignment.Vertical = Alignment.CenterVertically, unbounded: Boolean = false): Modifier = + this.then(WrapElement(false, true, Alignment { s, sp, _ -> androidx.compose.ui.unit.IntOffset(0, align.align(s.height, sp.height)) }, unbounded)) +fun Modifier.wrapContentSize(align: Alignment = Alignment.Center, unbounded: Boolean = false): Modifier = + this.then(WrapElement(true, true, align, unbounded)) + +fun Modifier.offset(x: Dp = Dp(0f), y: Dp = Dp(0f)): Modifier = this.then(OffsetElement(x, y)) + +fun Modifier.aspectRatio(ratio: Float): Modifier = this.then(AspectRatioElement(ratio)) diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/lazy/LazyList.kt new file mode 100644 index 00000000000..bb04c56b2bd --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/lazy/LazyList.kt @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.foundation.lazy + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ComposeNode +import androidx.compose.runtime.Composition +import androidx.compose.runtime.CompositionContext +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCompositionContext +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.codename1.compose.internal.BoxPolicy +import com.codename1.compose.internal.Constraints +import com.codename1.compose.internal.Density +import com.codename1.compose.internal.LTR +import com.codename1.compose.internal.NodeApplier +import com.codename1.compose.internal.UiNode +import com.codename1.compose.internal.h +import com.codename1.compose.internal.measureChild +import com.codename1.compose.internal.size +import com.codename1.compose.internal.w +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxHeight + +/// The receiver of an item's content. +@Stable +interface LazyItemScope { + fun Modifier.fillParentMaxWidth(fraction: Float = 1f): Modifier = this.fillMaxWidth(fraction) + fun Modifier.fillParentMaxHeight(fraction: Float = 1f): Modifier = this.fillMaxHeight(fraction) + fun Modifier.animateItem(): Modifier = this +} + +private object LazyItemScopeInstance : LazyItemScope + +/// What a [LazyColumn] or [LazyRow] content block declares. +interface LazyListScope { + fun item(key: Any? = null, contentType: Any? = null, content: @Composable LazyItemScope.() -> Unit) + fun items(count: Int, key: ((Int) -> Any)? = null, contentType: (Int) -> Any? = { null }, + itemContent: @Composable LazyItemScope.(Int) -> Unit) + fun stickyHeader(key: Any? = null, contentType: Any? = null, content: @Composable LazyItemScope.() -> Unit) = + item(key, contentType, content) +} + +inline fun LazyListScope.items(items: List, noinline key: ((T) -> Any)? = null, + crossinline itemContent: @Composable LazyItemScope.(T) -> Unit) { + items(items.size, if (key != null) { i: Int -> key(items[i]) } else null) { itemContent(items[it]) } +} + +inline fun LazyListScope.itemsIndexed(items: List, noinline key: ((Int, T) -> Any)? = null, + crossinline itemContent: @Composable LazyItemScope.(Int, T) -> Unit) { + items(items.size, if (key != null) { i: Int -> key(i, items[i]) } else null) { itemContent(it, items[it]) } +} + +private class Entry(val start: Int, val count: Int, val key: ((Int) -> Any)?, + val content: @Composable LazyItemScope.(Int) -> Unit) + +/// What one run of a lazy list's content block declared. Only the items in view +/// are ever composed from it. +internal class LazyItems : LazyListScope { + private val entries = ArrayList(2) + var count = 0 + private set + + override fun item(key: Any?, contentType: Any?, content: @Composable LazyItemScope.() -> Unit) { + entries.add(Entry(count, 1, if (key != null) { _: Int -> key } else null) { content() }) + count++ + } + + override fun items(count: Int, key: ((Int) -> Any)?, contentType: (Int) -> Any?, + itemContent: @Composable LazyItemScope.(Int) -> Unit) { + entries.add(Entry(this.count, count, key, itemContent)) + this.count += count + } + + private fun entryAt(index: Int): Entry { + var e = entries[0] + for (k in 1 until entries.size) { + val n = entries[k] + if (n.start > index) break + e = n + } + return e + } + + /// The item's key: the one the content block gave it, or its position. + fun keyAt(index: Int): Any { + val e = entryAt(index) + val k = e.key ?: return IndexKey(index) + return k(index - e.start) + } + + @Composable + fun Content(index: Int) { + val e = entryAt(index) + e.content(LazyItemScopeInstance, index - e.start) + } +} + +private data class IndexKey(val index: Int) + +/// The scroll position of a lazy list. +@Stable +class LazyListState internal constructor(internal val scroll: ScrollState) { + internal var node: LazyListNode? = null + + val firstVisibleItemIndex: Int get() = node?.cn1FirstVisible() ?: 0 + val firstVisibleItemScrollOffset: Int get() = scroll.value + val canScrollForward: Boolean get() = scroll.value < scroll.maxValue + val canScrollBackward: Boolean get() = scroll.value > 0 + + suspend fun scrollToItem(index: Int, scrollOffset: Int = 0) { + val n = node + if (n != null) n.cn1ScrollToItem(index, scrollOffset) else scroll.scrollTo(scrollOffset) + } + + suspend fun animateScrollToItem(index: Int, scrollOffset: Int = 0) = scrollToItem(index, scrollOffset) +} + +@Composable +fun rememberLazyListState(): LazyListState = remember { LazyListState(ScrollState(0)) } + +/// A vertically scrolling list that composes only the items in view, and +/// disposes of an item's composition once it has scrolled well out of it. +@Composable +fun LazyColumn( + modifier: Modifier = Modifier, + state: LazyListState = rememberLazyListState(), + contentPadding: PaddingValues = PaddingValues(), + reverseLayout: Boolean = false, + verticalArrangement: Arrangement.Vertical = Arrangement.Top, + horizontalAlignment: Alignment.Horizontal = Alignment.Start, + userScrollEnabled: Boolean = true, + content: LazyListScope.() -> Unit +) { + LazyList(true, modifier, state, contentPadding, verticalArrangement.spacing, horizontalAlignment, null, + reverseLayout, userScrollEnabled, content) +} + +@Composable +fun LazyRow( + modifier: Modifier = Modifier, + state: LazyListState = rememberLazyListState(), + contentPadding: PaddingValues = PaddingValues(), + reverseLayout: Boolean = false, + horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, + verticalAlignment: Alignment.Vertical = Alignment.Top, + userScrollEnabled: Boolean = true, + content: LazyListScope.() -> Unit +) { + LazyList(false, modifier, state, contentPadding, horizontalArrangement.spacing, null, verticalAlignment, + reverseLayout, userScrollEnabled, content) +} + +@Composable +private fun LazyList(vertical: Boolean, modifier: Modifier, state: LazyListState, padding: PaddingValues, + spacing: androidx.compose.ui.unit.Dp, horizontal: Alignment.Horizontal?, + verticalAlign: Alignment.Vertical?, reverseLayout: Boolean, userScrollEnabled: Boolean, + content: LazyListScope.() -> Unit) { + // Laying the items out from the end is not implemented. Refusing is better than drawing a + // chat-style list upside down and letting it look like it works. + require(!reverseLayout) { "reverseLayout = true is not supported by LazyColumn/LazyRow" } + val context = rememberCompositionContext() + val holder = remember { NodeHolder() } + DisposableEffect(holder) { + onDispose { holder.node?.cn1Dispose() } + } + // The declared items are rebuilt only when the content block changes or a state it read does. + // A parent recomposition that hands back the same block (the Compose compiler memoizes it when + // its captures are stable) reuses the same LazyItems, so `set(items)` below does not fire: no + // remap, and no item composition is given new content. + val items = remember(content) { derivedStateOf { LazyItems().also(content) } }.value + ComposeNode( + factory = { LazyListNode(vertical).also { holder.node = it } }, + update = { + set(context) { this.context = it } + set(state) { this.state = it } + set(padding) { this.padding = it } + set(spacing) { this.spacing = it } + set(horizontal) { this.horizontalAlignment = it } + set(verticalAlign) { this.verticalAlignment = it } + set(userScrollEnabled) { this.userScroll = it } + set(modifier) { this.modifier = it } + set(items) { this.items = it } + } + ) +} + +/// Lets the list's item compositions be disposed of with the list. +private class NodeHolder { + var node: LazyListNode? = null +} + +/// The node behind [LazyColumn] and [LazyRow]. It is its own scroll container, +/// and every item in view is a composition of its own under the list's, rooted +/// at a node that is this one's child. Item sizes are learnt as items are +/// measured; the extent of the ones never seen is estimated from the average. +/// +/// A pass costs the window, not the list: measured extents and their count are +/// kept in two Fenwick trees, so where any item starts is O(log n) and the item +/// at a position O(log^2 n), and the pass walks only the items within reach of +/// the view. Only a change of content is O(n), to carry sizes over by key. +internal class LazyListNode(private val vertical: Boolean) : UiNode() { + private class Slot(val key: Any, val root: UiNode, val composition: Composition) { + var index = -1 + var items: LazyItems? = null + var live = false + var start = 0 + } + + var context: CompositionContext? = null + var state: LazyListState? = null + set(v) { + if (field !== v) { + field?.node = null + field?.scroll?.handle?.detach(this) + field = v + v?.node = this + v?.scroll?.handle?.attach(this, vertical) + } + } + var padding: PaddingValues = PaddingValues() + set(v) { if (field != v) { field = v; invalidateMeasure() } } + var spacing: androidx.compose.ui.unit.Dp = androidx.compose.ui.unit.Dp(0f) + set(v) { if (field != v) { field = v; invalidateMeasure() } } + var userScroll = true + set(v) { + if (field != v) { + field = v + if (vertical) isScrollableY = v else isScrollableX = v + } + } + var horizontalAlignment: Alignment.Horizontal? = null + var verticalAlignment: Alignment.Vertical? = null + var items: LazyItems? = null + set(v) { + val old = field + field = v + if (v != null) remap(old, v) + invalidateMeasure() + } + + // Composed items, by key and as a list; the list is what a pass walks, so that + // nothing allocates an iterator. + private val slots = HashMap() + private val slotList = ArrayList() + // Per item: its key, and its extent along the scroll axis once measured (-1 before). + private var keys = arrayOfNulls(0) + private var extents = IntArray(0) + // Fenwick trees over the measured extents and over how many are measured. + private var sumTree = IntArray(1) + private var countTree = IntArray(1) + private var knownCount = 0 + private var knownSum = 0 + // The geometry of the last pass, which positions are computed with until the next. + private var lead = 0 + private var gap = 0 + private var estimate = 0 + private var total = 0 + private var crossExtent = 0 + private var windowAt = Int.MIN_VALUE + private var viewport = 0 + private var measuring = false + // The item the view is anchored on across a pass that learns sizes: the first one in view and + // how far into it the view starts. Without it an item above the view measured larger than its + // estimate pushes everything in view down, and the list jumps under a still finger. + private var anchorKey: Any? = null + private var anchorOffset = 0 + private var anchorIndex = -1 + // Set when the anchor was taken by a remap or by scrollToItem and must be used as it is; + // otherwise every pass takes it afresh from where the view is now. + private var anchorFixed = false + + init { + policy = BoxPolicy.TopStart + if (vertical) isScrollableY = true else isScrollableX = true + } + + override fun lazyAxis(): Int = if (!userScroll) 0 else if (vertical) 1 else 2 + + private fun treeAdd(t: IntArray, index: Int, v: Int) { + var i = index + 1 + while (i < t.size) { + t[i] += v + i += i and -i + } + } + + /// The sum over items [0, index). + private fun treeSum(t: IntArray, index: Int): Int { + var i = index + var s = 0 + while (i > 0) { + s += t[i] + i -= i and -i + } + return s + } + + private fun setExtent(i: Int, e: Int) { + val old = extents[i] + if (old == e) return + extents[i] = e + if (old < 0) { + treeAdd(countTree, i, 1) + knownCount++ + treeAdd(sumTree, i, e) + knownSum += e + } else { + treeAdd(sumTree, i, e - old) + knownSum += e - old + } + } + + /// Where item `i` starts (or, for `i == n`, where the gap after the last one ends). + private fun startOf(i: Int): Int { + val kc = treeSum(countTree, i) + // In Long and clamped: a list of millions of estimated items passes Int.MAX_VALUE pixels. + val v = lead.toLong() + treeSum(sumTree, i) + (i - kc).toLong() * estimate + i.toLong() * gap + return if (v > MAX_EXTENT) MAX_EXTENT.toInt() else v.toInt() + } + + private companion object { + // Below Constraints.INF, so a clamped extent never reads as "unbounded". + const val MAX_EXTENT = (Int.MAX_VALUE / 2).toLong() + } + + private fun extentOf(i: Int): Int = if (extents[i] >= 0) extents[i] else estimate + + /// The first item that ends past `at`, or -1 for an empty list. + private fun firstAt(at: Int): Int { + val n = keys.size + var lo = 0 + var hi = n - 1 + var found = -1 + while (lo <= hi) { + val mid = (lo + hi) ushr 1 + if (startOf(mid) + extentOf(mid) > at) { + found = mid + hi = mid - 1 + } else { + lo = mid + 1 + } + } + return found + } + + /// Carries what is known of each item over to a new content block, by key. + private fun remap(old: LazyItems?, now: LazyItems) { + captureAnchor() + anchorIndex = -1 + anchorFixed = true + val n = now.count + val oldKeys = keys + val oldExtents = extents + val newKeys = arrayOfNulls(n) + val newExtents = IntArray(n) + var byKey: HashMap? = null + val ak = anchorKey + for (i in 0 until n) { + val k = now.keyAt(i) + newKeys[i] = k + if (anchorIndex < 0 && ak != null && k == ak) anchorIndex = i + var e = -1 + if (i < oldKeys.size && oldKeys[i] == k) { + e = oldExtents[i] + } else if (old != null && oldKeys.isNotEmpty()) { + if (byKey == null) { + byKey = HashMap(oldKeys.size * 2) + for (j in oldKeys.indices) { + // The first of a repeated key wins; a repeat is refused once composed. + val ok = oldKeys[j]!! + if (!byKey.containsKey(ok)) byKey[ok] = j + } + } + val j = byKey[k] + if (j != null) e = oldExtents[j] + } + newExtents[i] = e + } + keys = newKeys + extents = IntArray(n) { -1 } + sumTree = IntArray(n + 1) + countTree = IntArray(n + 1) + knownCount = 0 + knownSum = 0 + for (i in 0 until n) { + if (newExtents[i] >= 0) setExtent(i, newExtents[i]) + } + } + + override fun measureContent(c: Constraints): Long { + val list = items + val ctx = context + val n = list?.count ?: 0 + if (list == null || ctx == null || n == 0) { + disposeAll() + total = 0 + return size(c.minWidth, c.minHeight) + } + measuring = true + try { + return measureItems(list, ctx, c, n) + } finally { + measuring = false + } + } + + private fun measureItems(list: LazyItems, ctx: CompositionContext, c: Constraints, n: Int): Long { + // The anchor is taken with the geometry the view was drawn with, before this pass + // changes the estimate. + if (!anchorFixed) { + captureAnchor() + } + val d = Density + val p = padding + lead = if (vertical) d.px(p.calculateTopPadding()) else d.px(p.calculateLeftPadding(LTR)) + val trail = if (vertical) d.px(p.calculateBottomPadding()) else d.px(p.calculateRightPadding(LTR)) + val crossLead = if (vertical) d.px(p.calculateLeftPadding(LTR)) else d.px(p.calculateTopPadding()) + val crossTrail = if (vertical) d.px(p.calculateRightPadding(LTR)) else d.px(p.calculateBottomPadding()) + gap = d.px(spacing) + // Fixed for the pass, so that every position in it is computed the same way. + estimate = if (knownCount > 0) knownSum / knownCount else d.px(androidx.compose.ui.unit.Dp(72f)) + val viewMain = if (vertical) c.maxHeight else c.maxWidth + val crossMax = if (vertical) c.maxWidth else c.maxHeight + val crossAvail = if (crossMax == Constraints.INF) Constraints.INF else Math.max(0, crossMax - crossLead - crossTrail) + val itemC = if (vertical) Constraints(0, crossAvail, 0, Constraints.INF) + else Constraints(0, Constraints.INF, 0, crossAvail) + val unbounded = viewMain == Constraints.INF + var at = if (vertical) scrollY else scrollX + val ai0 = anchorIndex + if (!unbounded && at > 0 && ai0 in 0 until n) { + // Where the anchor is under this pass's estimate. The estimate moves as sizes are + // learnt, and a window chosen from the old position can miss every item. + val want = Math.max(0, startOf(ai0) + anchorOffset) + if (want != at) { + at = want + if (vertical) scrollY = at else scrollX = at + } + } + // Compose what is in view and a quarter of a view either side; keep what was + // composed until it is half a view away, so a slow scroll does not churn. + val margin = if (unbounded) 0 else viewMain / 4 + val composeFrom = if (unbounded) Int.MIN_VALUE else at - margin + val composeTo = if (unbounded) Int.MAX_VALUE else at + viewMain + margin + val keepFrom = if (unbounded) Int.MIN_VALUE else at - 2 * margin + val keepTo = if (unbounded) Int.MAX_VALUE else at + viewMain + 2 * margin + for (k in 0 until slotList.size) slotList[k].live = false + var i = if (unbounded) 0 else Math.max(0, firstAt(keepFrom)) + var pos = startOf(i) + var cross = 0 + while (i < n && pos < keepTo) { + var e = extents[i] + val guess = if (e >= 0) e else estimate + val key = keys[i]!! + var slot = slots[key] + val overlaps = pos + guess > composeFrom && pos < composeTo + val kept = slot != null && pos + guess > keepFrom + if (overlaps || kept) { + if (slot == null) { + slot = compose(key, ctx) + } else if (slot.live) { + // Two items share a slot; the second would silently take the first's + // composition. Like Compose's own, the check covers the items composed. + throw IllegalArgumentException("Key " + key + " was already used. Every item of a " + + "lazy list must have a unique key.") + } + if (slot.items !== list || slot.index != i) { + slot.items = list + slot.index = i + val l = list + val idx = i + slot.composition.setContent { l.Content(idx) } + } + val m = measureChild(slot.root, itemC) + e = if (vertical) m.h() else m.w() + setExtent(i, e) + cross = Math.max(cross, if (vertical) m.w() else m.h()) + slot.live = true + slot.start = pos + } else { + e = guess + } + pos += e + gap + i++ + } + val end = startOf(n) - gap + trail + if (total != end) { + total = end + // Component caches the scroll extent and clamps setScrollY against it; without this + // the anchor below is clamped to the extent the estimate gave last pass. + setScrollSize(null) + } + disposeDead() + crossExtent = cross + crossLead + crossTrail + val crossSize = crossExtent + // Place what is composed; positions are along the whole scrolled extent. + for (k in 0 until slotList.size) { + val s = slotList[k] + val ci = getComponentIndex(s.root) + if (ci < 0) continue + val iw = s.root.measuredWidth + val ih = s.root.measuredHeight + if (vertical) { + val a = horizontalAlignment + val x = crossLead + (if (a == null || crossAvail == Constraints.INF) 0 else a.align(iw, crossAvail, LTR)) + place(ci, x, s.start, iw, ih) + } else { + val a = verticalAlignment + val y = crossLead + (if (a == null || crossAvail == Constraints.INF) 0 else a.align(ih, crossAvail)) + place(ci, s.start, y, iw, ih) + } + } + viewport = if (unbounded) total else viewMain + var now = at + val ai = anchorIndex + if (!unbounded && at > 0 && ai in 0 until n) { + val want = Math.max(0, Math.min(startOf(ai) + anchorOffset, total - viewport)) + if (want != at) { + now = want + if (vertical) scrollY = want else scrollX = want + } + } + windowAt = now + anchorFixed = false + return if (vertical) size(c.constrainWidth(crossSize), c.constrainHeight(total)) + else size(c.constrainWidth(total), c.constrainHeight(crossSize)) + } + + private fun compose(key: Any, ctx: CompositionContext): Slot { + val root = UiNode() + addComponent(root) + val s = Slot(key, root, Composition(NodeApplier(root), ctx)) + slots[key] = s + slotList.add(s) + return s + } + + private fun disposeDead() { + var w = 0 + for (k in 0 until slotList.size) { + val s = slotList[k] + if (s.live) { + slotList[w++] = s + } else { + slots.remove(s.key) + s.composition.dispose() + removeComponent(s.root) + } + } + while (slotList.size > w) slotList.removeAt(slotList.size - 1) + } + + private fun disposeAll() { + for (k in 0 until slotList.size) slotList[k].live = false + disposeDead() + } + + override fun calcScrollSize(): com.codename1.ui.geom.Dimension = + if (vertical) com.codename1.ui.geom.Dimension(Math.max(width, crossExtent), Math.max(height, total)) + else com.codename1.ui.geom.Dimension(Math.max(width, total), Math.max(height, crossExtent)) + + override fun onScrollY(scrollY: Int) { + super.onScrollY(scrollY) + if (vertical) scrolled(scrollY) + } + + override fun onScrollX(scrollX: Int) { + super.onScrollX(scrollX) + if (!vertical) scrolled(scrollX) + } + + /// Composes what scrolled into view before it is painted: a quarter of a view + /// of travel since the last pass. + private fun scrolled(at: Int) { + if (measuring || windowAt == Int.MIN_VALUE) return + if (Math.abs(at - windowAt) * 4 >= viewport) { + remeasureInPlace() + } + } + + private fun captureAnchor() { + val at = if (vertical) scrollY else scrollX + val i = firstAt(at) + if (i < 0) { + anchorKey = null + anchorIndex = -1 + return + } + anchorKey = keys[i] + anchorIndex = i + anchorOffset = at - startOf(i) + } + + internal fun cn1FirstVisible(): Int = Math.max(0, firstAt(if (vertical) scrollY else scrollX)) + + /// Scrolls so that `index` starts `offset` into the view. The item is composed and measured + /// first, so the position is its real one and not the estimate it had while out of view. + internal fun cn1ScrollToItem(index: Int, offset: Int) { + // A pass composes around where the estimate put the item; measuring its neighbours can + // move it, or the end of the list, so repeat until the position holds. Each pass leaves + // fewer estimates behind it, and two are the usual case. + for (pass in 0 until 4) { + if (index !in keys.indices) return + val t = Math.max(0, Math.min(startOf(index) + offset, total - viewport)) + anchorKey = keys[index] + anchorIndex = index + anchorOffset = offset + anchorFixed = true + if ((if (vertical) scrollY else scrollX) == t && pass > 0) return + if (vertical) scrollY = t else scrollX = t + measuring = true + try { + remeasureInPlace() + } finally { + measuring = false + } + } + } + + /// How many items the list has, and how many of them are composed. + fun cn1ItemCount(): Int = items?.count ?: 0 + + fun cn1ComposedCount(): Int = slots.size + + /// Disposes every item composition; the list is leaving the composition. + internal fun cn1Dispose() { + disposeAll() + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/shape/Shapes.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/shape/Shapes.kt new file mode 100644 index 00000000000..e4bea72456e --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/shape/Shapes.kt @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.foundation.shape + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection + +/// A corner's size, resolved against the shape's size. +@Immutable +interface CornerSize { + fun toPx(shapeSize: Size, density: Density): Float +} + +private data class DpCornerSize(val size: Dp) : CornerSize { + override fun toPx(shapeSize: Size, density: Density): Float = size.value * density.density +} + +private data class PercentCornerSize(val percent: Float) : CornerSize { + override fun toPx(shapeSize: Size, density: Density): Float = shapeSize.minDimension * percent / 100f +} + +private data class PxCornerSize(val px: Float) : CornerSize { + override fun toPx(shapeSize: Size, density: Density): Float = px +} + +fun CornerSize(size: Dp): CornerSize = DpCornerSize(size) +fun CornerSize(percent: Int): CornerSize = PercentCornerSize(percent.toFloat()) +fun CornerSize(size: Float): CornerSize = PxCornerSize(size) + +@Immutable +open class RoundedCornerShape( + val topStart: CornerSize, val topEnd: CornerSize, val bottomEnd: CornerSize, val bottomStart: CornerSize +) : Shape { + override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline { + val ts = topStart.toPx(size, density) + val te = topEnd.toPx(size, density) + val be = bottomEnd.toPx(size, density) + val bs = bottomStart.toPx(size, density) + if (ts + te + be + bs == 0f) return Outline.Rectangle(size.width, size.height) + return Outline.Rounded(size.width, size.height, ts, te, be, bs) + } + + override fun equals(other: Any?): Boolean = other is RoundedCornerShape && other.topStart == topStart && + other.topEnd == topEnd && other.bottomEnd == bottomEnd && other.bottomStart == bottomStart + + override fun hashCode(): Int = ((topStart.hashCode() * 31 + topEnd.hashCode()) * 31 + bottomEnd.hashCode()) * 31 + + bottomStart.hashCode() + + fun copy(topStart: CornerSize = this.topStart, topEnd: CornerSize = this.topEnd, bottomEnd: CornerSize = this.bottomEnd, + bottomStart: CornerSize = this.bottomStart): RoundedCornerShape = + RoundedCornerShape(topStart, topEnd, bottomEnd, bottomStart) +} + +fun RoundedCornerShape(corner: CornerSize): RoundedCornerShape = RoundedCornerShape(corner, corner, corner, corner) +fun RoundedCornerShape(size: Dp): RoundedCornerShape = RoundedCornerShape(CornerSize(size)) +fun RoundedCornerShape(percent: Int): RoundedCornerShape = RoundedCornerShape(CornerSize(percent)) +fun RoundedCornerShape(topStart: Dp = Dp(0f), topEnd: Dp = Dp(0f), bottomEnd: Dp = Dp(0f), bottomStart: Dp = Dp(0f)): RoundedCornerShape = + RoundedCornerShape(CornerSize(topStart), CornerSize(topEnd), CornerSize(bottomEnd), CornerSize(bottomStart)) + +val CircleShape: RoundedCornerShape = RoundedCornerShape(50) diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/text/BasicText.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/text/BasicText.kt new file mode 100644 index 00000000000..9283388ea8e --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/foundation/text/BasicText.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.foundation.text + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ComposeNode +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow +import com.codename1.compose.internal.NodeApplier +import com.codename1.compose.internal.TextNode + +@Composable +fun BasicText( + text: String, + modifier: Modifier = Modifier, + style: TextStyle = TextStyle.Default, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE +) { + ComposeNode( + factory = { TextNode() }, + update = { + set(modifier) { this.modifier = it } + set(text) { this.text = it } + set(style) { this.style = it } + set(overflow) { this.overflow = it } + set(softWrap) { this.softWrap = it } + set(maxLines) { this.maxLines = it } + } + ) +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/Icons.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/Icons.kt new file mode 100644 index 00000000000..b98bf58ff17 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/Icons.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material.icons + +/// The material icon sets. Each icon is an extension property in the set's +/// package (`androidx.compose.material.icons.filled.Favorite`), as in Compose. +object Icons { + object Filled + object Outlined + object Rounded + object AutoMirrored { + object Filled + object Outlined + } + + val Default: Filled = Filled +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/automirrored/filled/AutoMirrored.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/automirrored/filled/AutoMirrored.kt new file mode 100644 index 00000000000..3e8891b4453 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/automirrored/filled/AutoMirrored.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material.icons.automirrored.filled + +import androidx.compose.material.icons.Icons +import androidx.compose.ui.graphics.vector.ImageVector + +private val mirroredArrowBack: ImageVector = ImageVector.cn1Material("Filled.ArrowBack", '\uE5C4') +val Icons.AutoMirrored.Filled.ArrowBack: ImageVector get() = mirroredArrowBack + +private val mirroredKeyboardArrowRight: ImageVector = ImageVector.cn1Material("Filled.KeyboardArrowRight", '\uE315') +val Icons.AutoMirrored.Filled.KeyboardArrowRight: ImageVector get() = mirroredKeyboardArrowRight + +private val mirroredList: ImageVector = ImageVector.cn1Material("Filled.List", '\uE896') +val Icons.AutoMirrored.Filled.List: ImageVector get() = mirroredList + +private val mirroredSend: ImageVector = ImageVector.cn1Material("Filled.Send", '\uE163') +val Icons.AutoMirrored.Filled.Send: ImageVector get() = mirroredSend diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/filled/Filled.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/filled/Filled.kt new file mode 100644 index 00000000000..8250e8dbb32 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/filled/Filled.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material.icons.filled + +import androidx.compose.material.icons.Icons +import androidx.compose.ui.graphics.vector.ImageVector + +private val filledFavorite: ImageVector = ImageVector.cn1Material("Filled.Favorite", '\uE87D') +val Icons.Filled.Favorite: ImageVector get() = filledFavorite + +private val filledFavoriteBorder: ImageVector = ImageVector.cn1Material("Filled.FavoriteBorder", '\uE87E') +val Icons.Filled.FavoriteBorder: ImageVector get() = filledFavoriteBorder + +private val filledSearch: ImageVector = ImageVector.cn1Material("Filled.Search", '\uE8B6') +val Icons.Filled.Search: ImageVector get() = filledSearch + +private val filledClose: ImageVector = ImageVector.cn1Material("Filled.Close", '\uE5CD') +val Icons.Filled.Close: ImageVector get() = filledClose + +private val filledArrowBack: ImageVector = ImageVector.cn1Material("Filled.ArrowBack", '\uE5C4') +val Icons.Filled.ArrowBack: ImageVector get() = filledArrowBack + +private val filledAdd: ImageVector = ImageVector.cn1Material("Filled.Add", '\uE145') +val Icons.Filled.Add: ImageVector get() = filledAdd + +private val filledHome: ImageVector = ImageVector.cn1Material("Filled.Home", '\uE88A') +val Icons.Filled.Home: ImageVector get() = filledHome + +private val filledPerson: ImageVector = ImageVector.cn1Material("Filled.Person", '\uE7FD') +val Icons.Filled.Person: ImageVector get() = filledPerson + +private val filledSettings: ImageVector = ImageVector.cn1Material("Filled.Settings", '\uE8B8') +val Icons.Filled.Settings: ImageVector get() = filledSettings + +private val filledStar: ImageVector = ImageVector.cn1Material("Filled.Star", '\uE838') +val Icons.Filled.Star: ImageVector get() = filledStar + +private val filledStarBorder: ImageVector = ImageVector.cn1Material("Filled.StarBorder", '\uE83A') +val Icons.Filled.StarBorder: ImageVector get() = filledStarBorder + +private val filledMenu: ImageVector = ImageVector.cn1Material("Filled.Menu", '\uE5D2') +val Icons.Filled.Menu: ImageVector get() = filledMenu + +private val filledMoreVert: ImageVector = ImageVector.cn1Material("Filled.MoreVert", '\uE5D4') +val Icons.Filled.MoreVert: ImageVector get() = filledMoreVert + +private val filledCheck: ImageVector = ImageVector.cn1Material("Filled.Check", '\uE5CA') +val Icons.Filled.Check: ImageVector get() = filledCheck + +private val filledInfo: ImageVector = ImageVector.cn1Material("Filled.Info", '\uE88E') +val Icons.Filled.Info: ImageVector get() = filledInfo + +private val filledSchedule: ImageVector = ImageVector.cn1Material("Filled.Schedule", '\uE8B5') +val Icons.Filled.Schedule: ImageVector get() = filledSchedule + +private val filledPlace: ImageVector = ImageVector.cn1Material("Filled.Place", '\uE55F') +val Icons.Filled.Place: ImageVector get() = filledPlace + +private val filledShare: ImageVector = ImageVector.cn1Material("Filled.Share", '\uE80D') +val Icons.Filled.Share: ImageVector get() = filledShare + +private val filledDelete: ImageVector = ImageVector.cn1Material("Filled.Delete", '\uE872') +val Icons.Filled.Delete: ImageVector get() = filledDelete + +private val filledEdit: ImageVector = ImageVector.cn1Material("Filled.Edit", '\uE3C9') +val Icons.Filled.Edit: ImageVector get() = filledEdit + +private val filledRefresh: ImageVector = ImageVector.cn1Material("Filled.Refresh", '\uE5D5') +val Icons.Filled.Refresh: ImageVector get() = filledRefresh + +private val filledNotifications: ImageVector = ImageVector.cn1Material("Filled.Notifications", '\uE7F4') +val Icons.Filled.Notifications: ImageVector get() = filledNotifications + +private val filledDateRange: ImageVector = ImageVector.cn1Material("Filled.DateRange", '\uE916') +val Icons.Filled.DateRange: ImageVector get() = filledDateRange + +private val filledList: ImageVector = ImageVector.cn1Material("Filled.List", '\uE896') +val Icons.Filled.List: ImageVector get() = filledList + +private val filledThumbUp: ImageVector = ImageVector.cn1Material("Filled.ThumbUp", '\uE8DC') +val Icons.Filled.ThumbUp: ImageVector get() = filledThumbUp + +private val filledPlayArrow: ImageVector = ImageVector.cn1Material("Filled.PlayArrow", '\uE037') +val Icons.Filled.PlayArrow: ImageVector get() = filledPlayArrow + +private val filledKeyboardArrowRight: ImageVector = ImageVector.cn1Material("Filled.KeyboardArrowRight", '\uE315') +val Icons.Filled.KeyboardArrowRight: ImageVector get() = filledKeyboardArrowRight + +private val filledKeyboardArrowDown: ImageVector = ImageVector.cn1Material("Filled.KeyboardArrowDown", '\uE313') +val Icons.Filled.KeyboardArrowDown: ImageVector get() = filledKeyboardArrowDown + +private val filledAccountCircle: ImageVector = ImageVector.cn1Material("Filled.AccountCircle", '\uE853') +val Icons.Filled.AccountCircle: ImageVector get() = filledAccountCircle + +private val filledCalendarToday: ImageVector = ImageVector.cn1Material("Filled.CalendarToday", '\uE935') +val Icons.Filled.CalendarToday: ImageVector get() = filledCalendarToday + +private val filledEvent: ImageVector = ImageVector.cn1Material("Filled.Event", '\uE878') +val Icons.Filled.Event: ImageVector get() = filledEvent + +private val filledMic: ImageVector = ImageVector.cn1Material("Filled.Mic", '\uE029') +val Icons.Filled.Mic: ImageVector get() = filledMic + +private val filledBookmark: ImageVector = ImageVector.cn1Material("Filled.Bookmark", '\uE866') +val Icons.Filled.Bookmark: ImageVector get() = filledBookmark + +private val filledBookmarkBorder: ImageVector = ImageVector.cn1Material("Filled.BookmarkBorder", '\uE867') +val Icons.Filled.BookmarkBorder: ImageVector get() = filledBookmarkBorder + +private val filledFilterList: ImageVector = ImageVector.cn1Material("Filled.FilterList", '\uE152') +val Icons.Filled.FilterList: ImageVector get() = filledFilterList + +private val filledClear: ImageVector = ImageVector.cn1Material("Filled.Clear", '\uE14C') +val Icons.Filled.Clear: ImageVector get() = filledClear + +private val filledSend: ImageVector = ImageVector.cn1Material("Filled.Send", '\uE163') +val Icons.Filled.Send: ImageVector get() = filledSend + +private val filledLocationOn: ImageVector = ImageVector.cn1Material("Filled.LocationOn", '\uE0C8') +val Icons.Filled.LocationOn: ImageVector get() = filledLocationOn + +private val filledEmail: ImageVector = ImageVector.cn1Material("Filled.Email", '\uE0BE') +val Icons.Filled.Email: ImageVector get() = filledEmail + +private val filledPhone: ImageVector = ImageVector.cn1Material("Filled.Phone", '\uE0CD') +val Icons.Filled.Phone: ImageVector get() = filledPhone + +private val filledWarning: ImageVector = ImageVector.cn1Material("Filled.Warning", '\uE002') +val Icons.Filled.Warning: ImageVector get() = filledWarning diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/outlined/Outlined.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/outlined/Outlined.kt new file mode 100644 index 00000000000..b8a6c885d53 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material/icons/outlined/Outlined.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material.icons.outlined + +import androidx.compose.material.icons.Icons +import androidx.compose.ui.graphics.vector.ImageVector + +private val outlinedFavorite: ImageVector = ImageVector.cn1Material("Outlined.Favorite", '\uE87E') +val Icons.Outlined.Favorite: ImageVector get() = outlinedFavorite + +private val outlinedFavoriteBorder: ImageVector = ImageVector.cn1Material("Outlined.FavoriteBorder", '\uE87E') +val Icons.Outlined.FavoriteBorder: ImageVector get() = outlinedFavoriteBorder + +private val outlinedSearch: ImageVector = ImageVector.cn1Material("Outlined.Search", '\uE8B6') +val Icons.Outlined.Search: ImageVector get() = outlinedSearch + +private val outlinedClose: ImageVector = ImageVector.cn1Material("Outlined.Close", '\uE5CD') +val Icons.Outlined.Close: ImageVector get() = outlinedClose + +private val outlinedArrowBack: ImageVector = ImageVector.cn1Material("Outlined.ArrowBack", '\uE5C4') +val Icons.Outlined.ArrowBack: ImageVector get() = outlinedArrowBack + +private val outlinedAdd: ImageVector = ImageVector.cn1Material("Outlined.Add", '\uE145') +val Icons.Outlined.Add: ImageVector get() = outlinedAdd + +private val outlinedHome: ImageVector = ImageVector.cn1Material("Outlined.Home", '\uE88A') +val Icons.Outlined.Home: ImageVector get() = outlinedHome + +private val outlinedPerson: ImageVector = ImageVector.cn1Material("Outlined.Person", '\uE7FD') +val Icons.Outlined.Person: ImageVector get() = outlinedPerson + +private val outlinedSettings: ImageVector = ImageVector.cn1Material("Outlined.Settings", '\uE8B8') +val Icons.Outlined.Settings: ImageVector get() = outlinedSettings + +private val outlinedStar: ImageVector = ImageVector.cn1Material("Outlined.Star", '\uE83A') +val Icons.Outlined.Star: ImageVector get() = outlinedStar + +private val outlinedStarBorder: ImageVector = ImageVector.cn1Material("Outlined.StarBorder", '\uE83A') +val Icons.Outlined.StarBorder: ImageVector get() = outlinedStarBorder + +private val outlinedMenu: ImageVector = ImageVector.cn1Material("Outlined.Menu", '\uE5D2') +val Icons.Outlined.Menu: ImageVector get() = outlinedMenu + +private val outlinedMoreVert: ImageVector = ImageVector.cn1Material("Outlined.MoreVert", '\uE5D4') +val Icons.Outlined.MoreVert: ImageVector get() = outlinedMoreVert + +private val outlinedCheck: ImageVector = ImageVector.cn1Material("Outlined.Check", '\uE5CA') +val Icons.Outlined.Check: ImageVector get() = outlinedCheck + +private val outlinedInfo: ImageVector = ImageVector.cn1Material("Outlined.Info", '\uE88E') +val Icons.Outlined.Info: ImageVector get() = outlinedInfo + +private val outlinedSchedule: ImageVector = ImageVector.cn1Material("Outlined.Schedule", '\uE8B5') +val Icons.Outlined.Schedule: ImageVector get() = outlinedSchedule + +private val outlinedPlace: ImageVector = ImageVector.cn1Material("Outlined.Place", '\uE55F') +val Icons.Outlined.Place: ImageVector get() = outlinedPlace + +private val outlinedShare: ImageVector = ImageVector.cn1Material("Outlined.Share", '\uE80D') +val Icons.Outlined.Share: ImageVector get() = outlinedShare + +private val outlinedDelete: ImageVector = ImageVector.cn1Material("Outlined.Delete", '\uE872') +val Icons.Outlined.Delete: ImageVector get() = outlinedDelete + +private val outlinedEdit: ImageVector = ImageVector.cn1Material("Outlined.Edit", '\uE3C9') +val Icons.Outlined.Edit: ImageVector get() = outlinedEdit + +private val outlinedRefresh: ImageVector = ImageVector.cn1Material("Outlined.Refresh", '\uE5D5') +val Icons.Outlined.Refresh: ImageVector get() = outlinedRefresh + +private val outlinedNotifications: ImageVector = ImageVector.cn1Material("Outlined.Notifications", '\uE7F4') +val Icons.Outlined.Notifications: ImageVector get() = outlinedNotifications + +private val outlinedDateRange: ImageVector = ImageVector.cn1Material("Outlined.DateRange", '\uE916') +val Icons.Outlined.DateRange: ImageVector get() = outlinedDateRange + +private val outlinedList: ImageVector = ImageVector.cn1Material("Outlined.List", '\uE896') +val Icons.Outlined.List: ImageVector get() = outlinedList + +private val outlinedThumbUp: ImageVector = ImageVector.cn1Material("Outlined.ThumbUp", '\uE8DC') +val Icons.Outlined.ThumbUp: ImageVector get() = outlinedThumbUp + +private val outlinedPlayArrow: ImageVector = ImageVector.cn1Material("Outlined.PlayArrow", '\uE037') +val Icons.Outlined.PlayArrow: ImageVector get() = outlinedPlayArrow + +private val outlinedKeyboardArrowRight: ImageVector = ImageVector.cn1Material("Outlined.KeyboardArrowRight", '\uE315') +val Icons.Outlined.KeyboardArrowRight: ImageVector get() = outlinedKeyboardArrowRight + +private val outlinedKeyboardArrowDown: ImageVector = ImageVector.cn1Material("Outlined.KeyboardArrowDown", '\uE313') +val Icons.Outlined.KeyboardArrowDown: ImageVector get() = outlinedKeyboardArrowDown + +private val outlinedAccountCircle: ImageVector = ImageVector.cn1Material("Outlined.AccountCircle", '\uE853') +val Icons.Outlined.AccountCircle: ImageVector get() = outlinedAccountCircle + +private val outlinedCalendarToday: ImageVector = ImageVector.cn1Material("Outlined.CalendarToday", '\uE935') +val Icons.Outlined.CalendarToday: ImageVector get() = outlinedCalendarToday + +private val outlinedEvent: ImageVector = ImageVector.cn1Material("Outlined.Event", '\uE878') +val Icons.Outlined.Event: ImageVector get() = outlinedEvent + +private val outlinedMic: ImageVector = ImageVector.cn1Material("Outlined.Mic", '\uE029') +val Icons.Outlined.Mic: ImageVector get() = outlinedMic + +private val outlinedBookmark: ImageVector = ImageVector.cn1Material("Outlined.Bookmark", '\uE867') +val Icons.Outlined.Bookmark: ImageVector get() = outlinedBookmark + +private val outlinedBookmarkBorder: ImageVector = ImageVector.cn1Material("Outlined.BookmarkBorder", '\uE867') +val Icons.Outlined.BookmarkBorder: ImageVector get() = outlinedBookmarkBorder + +private val outlinedFilterList: ImageVector = ImageVector.cn1Material("Outlined.FilterList", '\uE152') +val Icons.Outlined.FilterList: ImageVector get() = outlinedFilterList + +private val outlinedClear: ImageVector = ImageVector.cn1Material("Outlined.Clear", '\uE14C') +val Icons.Outlined.Clear: ImageVector get() = outlinedClear + +private val outlinedSend: ImageVector = ImageVector.cn1Material("Outlined.Send", '\uE163') +val Icons.Outlined.Send: ImageVector get() = outlinedSend + +private val outlinedLocationOn: ImageVector = ImageVector.cn1Material("Outlined.LocationOn", '\uE0C8') +val Icons.Outlined.LocationOn: ImageVector get() = outlinedLocationOn + +private val outlinedEmail: ImageVector = ImageVector.cn1Material("Outlined.Email", '\uE0BE') +val Icons.Outlined.Email: ImageVector get() = outlinedEmail + +private val outlinedPhone: ImageVector = ImageVector.cn1Material("Outlined.Phone", '\uE0CD') +val Icons.Outlined.Phone: ImageVector get() = outlinedPhone + +private val outlinedWarning: ImageVector = ImageVector.cn1Material("Outlined.Warning", '\uE002') +val Icons.Outlined.Warning: ImageVector get() = outlinedWarning diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material3/ColorScheme.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/ColorScheme.kt new file mode 100644 index 00000000000..cd040b70809 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/ColorScheme.kt @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material3 + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +/// The Material 3 color roles. The defaults are the baseline scheme. +@Immutable +data class ColorScheme( val primary: Color, + val onPrimary: Color, + val primaryContainer: Color, + val onPrimaryContainer: Color, + val inversePrimary: Color, + val secondary: Color, + val onSecondary: Color, + val secondaryContainer: Color, + val onSecondaryContainer: Color, + val tertiary: Color, + val onTertiary: Color, + val tertiaryContainer: Color, + val onTertiaryContainer: Color, + val background: Color, + val onBackground: Color, + val surface: Color, + val onSurface: Color, + val surfaceVariant: Color, + val onSurfaceVariant: Color, + val surfaceTint: Color, + val inverseSurface: Color, + val inverseOnSurface: Color, + val error: Color, + val onError: Color, + val errorContainer: Color, + val onErrorContainer: Color, + val outline: Color, + val outlineVariant: Color, + val scrim: Color, + val surfaceBright: Color, + val surfaceDim: Color, + val surfaceContainer: Color, + val surfaceContainerHigh: Color, + val surfaceContainerHighest: Color, + val surfaceContainerLow: Color, + val surfaceContainerLowest: Color) + +fun lightColorScheme( primary: Color = Color(0xFF6750A4), + onPrimary: Color = Color(0xFFFFFFFF), + primaryContainer: Color = Color(0xFFEADDFF), + onPrimaryContainer: Color = Color(0xFF21005D), + inversePrimary: Color = Color(0xFFD0BCFF), + secondary: Color = Color(0xFF625B71), + onSecondary: Color = Color(0xFFFFFFFF), + secondaryContainer: Color = Color(0xFFE8DEF8), + onSecondaryContainer: Color = Color(0xFF1D192B), + tertiary: Color = Color(0xFF7D5260), + onTertiary: Color = Color(0xFFFFFFFF), + tertiaryContainer: Color = Color(0xFFFFD8E4), + onTertiaryContainer: Color = Color(0xFF31111D), + background: Color = Color(0xFFFEF7FF), + onBackground: Color = Color(0xFF1D1B20), + surface: Color = Color(0xFFFEF7FF), + onSurface: Color = Color(0xFF1D1B20), + surfaceVariant: Color = Color(0xFFE7E0EC), + onSurfaceVariant: Color = Color(0xFF49454F), + surfaceTint: Color = Color(0xFF6750A4), + inverseSurface: Color = Color(0xFF322F35), + inverseOnSurface: Color = Color(0xFFF5EFF7), + error: Color = Color(0xFFB3261E), + onError: Color = Color(0xFFFFFFFF), + errorContainer: Color = Color(0xFFF9DEDC), + onErrorContainer: Color = Color(0xFF410E0B), + outline: Color = Color(0xFF79747E), + outlineVariant: Color = Color(0xFFCAC4D0), + scrim: Color = Color(0xFF000000), + surfaceBright: Color = Color(0xFFFEF7FF), + surfaceDim: Color = Color(0xFFDED8E1), + surfaceContainer: Color = Color(0xFFF3EDF7), + surfaceContainerHigh: Color = Color(0xFFECE6F0), + surfaceContainerHighest: Color = Color(0xFFE6E0E9), + surfaceContainerLow: Color = Color(0xFFF7F2FA), + surfaceContainerLowest: Color = Color(0xFFFFFFFF)): ColorScheme = ColorScheme(primary, onPrimary, primaryContainer, onPrimaryContainer, inversePrimary, secondary, onSecondary, secondaryContainer, onSecondaryContainer, tertiary, onTertiary, tertiaryContainer, onTertiaryContainer, background, onBackground, surface, onSurface, surfaceVariant, onSurfaceVariant, surfaceTint, inverseSurface, inverseOnSurface, error, onError, errorContainer, onErrorContainer, outline, outlineVariant, scrim, surfaceBright, surfaceDim, surfaceContainer, surfaceContainerHigh, surfaceContainerHighest, surfaceContainerLow, surfaceContainerLowest) + +fun darkColorScheme( primary: Color = Color(0xFFD0BCFF), + onPrimary: Color = Color(0xFF381E72), + primaryContainer: Color = Color(0xFF4F378B), + onPrimaryContainer: Color = Color(0xFFEADDFF), + inversePrimary: Color = Color(0xFF6750A4), + secondary: Color = Color(0xFFCCC2DC), + onSecondary: Color = Color(0xFF332D41), + secondaryContainer: Color = Color(0xFF4A4458), + onSecondaryContainer: Color = Color(0xFFE8DEF8), + tertiary: Color = Color(0xFFEFB8C8), + onTertiary: Color = Color(0xFF492532), + tertiaryContainer: Color = Color(0xFF633B48), + onTertiaryContainer: Color = Color(0xFFFFD8E4), + background: Color = Color(0xFF141218), + onBackground: Color = Color(0xFFE6E0E9), + surface: Color = Color(0xFF141218), + onSurface: Color = Color(0xFFE6E0E9), + surfaceVariant: Color = Color(0xFF49454F), + onSurfaceVariant: Color = Color(0xFFCAC4D0), + surfaceTint: Color = Color(0xFFD0BCFF), + inverseSurface: Color = Color(0xFFE6E0E9), + inverseOnSurface: Color = Color(0xFF322F35), + error: Color = Color(0xFFF2B8B5), + onError: Color = Color(0xFF601410), + errorContainer: Color = Color(0xFF8C1D18), + onErrorContainer: Color = Color(0xFFF9DEDC), + outline: Color = Color(0xFF938F99), + outlineVariant: Color = Color(0xFF49454F), + scrim: Color = Color(0xFF000000), + surfaceBright: Color = Color(0xFF3B383E), + surfaceDim: Color = Color(0xFF141218), + surfaceContainer: Color = Color(0xFF211F26), + surfaceContainerHigh: Color = Color(0xFF2B2930), + surfaceContainerHighest: Color = Color(0xFF36343B), + surfaceContainerLow: Color = Color(0xFF1D1B20), + surfaceContainerLowest: Color = Color(0xFF0F0D13)): ColorScheme = ColorScheme(primary, onPrimary, primaryContainer, onPrimaryContainer, inversePrimary, secondary, onSecondary, secondaryContainer, onSecondaryContainer, tertiary, onTertiary, tertiaryContainer, onTertiaryContainer, background, onBackground, surface, onSurface, surfaceVariant, onSurfaceVariant, surfaceTint, inverseSurface, inverseOnSurface, error, onError, errorContainer, onErrorContainer, outline, outlineVariant, scrim, surfaceBright, surfaceDim, surfaceContainer, surfaceContainerHigh, surfaceContainerHighest, surfaceContainerLow, surfaceContainerLowest) + +/// The color that goes on `background`: its `on` role, or the content color in +/// effect when the color is not one of the scheme's. +fun ColorScheme.contentColorFor(backgroundColor: Color): Color = when (backgroundColor) { + primary -> onPrimary + secondary -> onSecondary + tertiary -> onTertiary + background -> onBackground + error -> onError + primaryContainer -> onPrimaryContainer + secondaryContainer -> onSecondaryContainer + tertiaryContainer -> onTertiaryContainer + errorContainer -> onErrorContainer + inverseSurface -> inverseOnSurface + surface, surfaceContainer, surfaceContainerHigh, surfaceContainerHighest, surfaceContainerLow, + surfaceContainerLowest, surfaceBright, surfaceDim -> onSurface + surfaceVariant -> onSurfaceVariant + else -> Color.Unspecified +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Components.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Components.kt new file mode 100644 index 00000000000..b8ae21b434d --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Components.kt @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material3 + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ComposeNode +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.Immutable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp +import androidx.compose.animation.core.animateFloat +import com.codename1.compose.internal.IconNode +import com.codename1.compose.internal.NodeApplier + +// --- text and icons --------------------------------------------------------- + +@Composable +fun Text( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontWeight: FontWeight? = null, + textAlign: TextAlign = TextAlign.Unspecified, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + style: TextStyle = LocalTextStyle.current +) { + val c = if (color != Color.Unspecified) color else if (style.color != Color.Unspecified) style.color else LocalContentColor.current + val merged = style.merge(TextStyle(color = c, fontSize = fontSize, fontWeight = fontWeight, textAlign = textAlign, + lineHeight = lineHeight)) + BasicText(text, modifier, merged, overflow, softWrap, maxLines) +} + +@Composable +fun Icon(imageVector: ImageVector, contentDescription: String?, modifier: Modifier = Modifier, + tint: Color = LocalContentColor.current) { + ComposeNode( + factory = { IconNode() }, + update = { + set(modifier) { this.modifier = it } + set(imageVector) { this.glyph = com.codename1.compose.internal.Glyphs.of(it) } + set(tint) { this.tint = it } + } + ) +} + +// --- surfaces --------------------------------------------------------------- + +@Composable +fun Surface( + modifier: Modifier = Modifier, + shape: Shape = RectangleShape, + color: Color = MaterialTheme.colorScheme.surface, + contentColor: Color = contentColorFor(color), + tonalElevation: Dp = 0.dp, + shadowElevation: Dp = 0.dp, + border: BorderStroke? = null, + content: @Composable () -> Unit +) { + val m = modifier.surface(shape, tint(color, tonalElevation), border) + CompositionLocalProvider(LocalContentColor provides contentColor) { + Box(m, propagateMinConstraints = true) { content() } + } +} + +@Composable +fun Surface( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + shape: Shape = RectangleShape, + color: Color = MaterialTheme.colorScheme.surface, + contentColor: Color = contentColorFor(color), + tonalElevation: Dp = 0.dp, + shadowElevation: Dp = 0.dp, + border: BorderStroke? = null, + content: @Composable () -> Unit +) { + val m = modifier.surface(shape, tint(color, tonalElevation), border).clickable(enabled, onClick = onClick) + CompositionLocalProvider(LocalContentColor provides contentColor) { + Box(m, propagateMinConstraints = true) { content() } + } +} + +private fun Modifier.surface(shape: Shape, color: Color, border: BorderStroke?): Modifier { + var m = this.clip(shape).background(color, shape) + if (border != null) m = m.border(border.width, border.color, shape) + return m +} + +@Composable +private fun tint(color: Color, elevation: Dp): Color { + val cs = MaterialTheme.colorScheme + if (color != cs.surface || elevation.value <= 0f) return color + val f = Math.min(0.14f, elevation.value * 0.02f) + return androidx.compose.ui.graphics.lerp(color, cs.surfaceTint, f) +} + +@Immutable +class CardColors(val containerColor: Color, val contentColor: Color) + +object CardDefaults { + val shape: Shape @Composable get() = MaterialTheme.shapes.medium + + @Composable + fun cardColors(containerColor: Color = MaterialTheme.colorScheme.surfaceContainerHighest, + contentColor: Color = contentColorFor(containerColor)): CardColors = CardColors(containerColor, contentColor) + + @Composable + fun elevatedCardColors(containerColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, + contentColor: Color = contentColorFor(containerColor)): CardColors = CardColors(containerColor, contentColor) + + @Composable + fun outlinedCardColors(containerColor: Color = MaterialTheme.colorScheme.surface, + contentColor: Color = contentColorFor(containerColor)): CardColors = CardColors(containerColor, contentColor) + + @Composable + fun cardElevation(defaultElevation: Dp = 0.dp): Dp = defaultElevation + + @Composable + fun elevatedCardElevation(defaultElevation: Dp = 1.dp): Dp = defaultElevation +} + +@Composable +fun Card(modifier: Modifier = Modifier, shape: Shape = CardDefaults.shape, colors: CardColors = CardDefaults.cardColors(), + elevation: Dp = 0.dp, border: BorderStroke? = null, content: @Composable ColumnScope.() -> Unit) { + Surface(modifier, shape, colors.containerColor, colors.contentColor, elevation, border = border) { + Column(content = content) + } +} + +@Composable +fun Card(onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, shape: Shape = CardDefaults.shape, + colors: CardColors = CardDefaults.cardColors(), elevation: Dp = 0.dp, border: BorderStroke? = null, + content: @Composable ColumnScope.() -> Unit) { + Surface(onClick, modifier, enabled, shape, colors.containerColor, colors.contentColor, elevation, border = border) { + Column(content = content) + } +} + +@Composable +fun ElevatedCard(modifier: Modifier = Modifier, shape: Shape = CardDefaults.shape, + colors: CardColors = CardDefaults.elevatedCardColors(), content: @Composable ColumnScope.() -> Unit) = + Card(modifier, shape, colors, 1.dp, null, content) + +@Composable +fun OutlinedCard(modifier: Modifier = Modifier, shape: Shape = CardDefaults.shape, + colors: CardColors = CardDefaults.outlinedCardColors(), content: @Composable ColumnScope.() -> Unit) = + Card(modifier, shape, colors, 0.dp, BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant), content) + +// --- buttons ---------------------------------------------------------------- + +@Immutable +class ButtonColors(val containerColor: Color, val contentColor: Color, val disabledContainerColor: Color, + val disabledContentColor: Color) { + internal fun container(enabled: Boolean) = if (enabled) containerColor else disabledContainerColor + internal fun content(enabled: Boolean) = if (enabled) contentColor else disabledContentColor +} + +object ButtonDefaults { + val ContentPadding: PaddingValues = PaddingValues(start = 24.dp, top = 8.dp, end = 24.dp, bottom = 8.dp) + val TextButtonContentPadding: PaddingValues = PaddingValues(start = 12.dp, top = 8.dp, end = 12.dp, bottom = 8.dp) + val MinWidth: Dp = 58.dp + val MinHeight: Dp = 40.dp + val IconSize: Dp = 18.dp + val IconSpacing: Dp = 8.dp + val shape: Shape @Composable get() = CircleShape + + @Composable + fun buttonColors(containerColor: Color = MaterialTheme.colorScheme.primary, + contentColor: Color = MaterialTheme.colorScheme.onPrimary, + disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f), + disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)): ButtonColors = + ButtonColors(containerColor, contentColor, disabledContainerColor, disabledContentColor) + + @Composable + fun filledTonalButtonColors(containerColor: Color = MaterialTheme.colorScheme.secondaryContainer, + contentColor: Color = MaterialTheme.colorScheme.onSecondaryContainer): ButtonColors = + buttonColors(containerColor, contentColor) + + @Composable + fun outlinedButtonColors(containerColor: Color = Color.Transparent, + contentColor: Color = MaterialTheme.colorScheme.primary): ButtonColors = + ButtonColors(containerColor, contentColor, Color.Transparent, MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)) + + @Composable + fun textButtonColors(containerColor: Color = Color.Transparent, + contentColor: Color = MaterialTheme.colorScheme.primary): ButtonColors = + ButtonColors(containerColor, contentColor, Color.Transparent, MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)) + + @Composable + fun elevatedButtonColors(containerColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, + contentColor: Color = MaterialTheme.colorScheme.primary): ButtonColors = + buttonColors(containerColor, contentColor) +} + +@Composable +fun Button( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + shape: Shape = ButtonDefaults.shape, + colors: ButtonColors = ButtonDefaults.buttonColors(), + border: BorderStroke? = null, + contentPadding: PaddingValues = ButtonDefaults.ContentPadding, + content: @Composable RowScope.() -> Unit +) { + Surface(onClick, modifier, enabled, shape, colors.container(enabled), colors.content(enabled), border = border) { + ProvideTextStyle(MaterialTheme.typography.labelLarge) { + Row(Modifier.defaultMinSize(ButtonDefaults.MinWidth, ButtonDefaults.MinHeight).padding(contentPadding), + horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, content = content) + } + } +} + +@Composable +fun FilledTonalButton(onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(), + contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit) = + Button(onClick, modifier, enabled, shape, colors, null, contentPadding, content) + +@Composable +fun ElevatedButton(onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.elevatedButtonColors(), + contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit) = + Button(onClick, modifier, enabled, shape, colors, null, contentPadding, content) + +@Composable +fun OutlinedButton(onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.outlinedButtonColors(), + border: BorderStroke? = BorderStroke(1.dp, MaterialTheme.colorScheme.outline), + contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit) = + Button(onClick, modifier, enabled, shape, colors, border, contentPadding, content) + +@Composable +fun TextButton(onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.textButtonColors(), + contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding, content: @Composable RowScope.() -> Unit) = + Button(onClick, modifier, enabled, shape, colors, null, contentPadding, content) + +@Composable +fun IconButton(onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, content: @Composable () -> Unit) { + val color = if (enabled) LocalContentColor.current else LocalContentColor.current.copy(alpha = 0.38f) + Box(modifier.size(40.dp).clip(CircleShape).clickable(enabled, onClick = onClick), contentAlignment = Alignment.Center) { + CompositionLocalProvider(LocalContentColor provides color) { content() } + } +} + +@Composable +fun FloatingActionButton(onClick: () -> Unit, modifier: Modifier = Modifier, + shape: Shape = MaterialTheme.shapes.large, + containerColor: Color = MaterialTheme.colorScheme.primaryContainer, + contentColor: Color = contentColorFor(containerColor), + content: @Composable () -> Unit) { + Surface(onClick, modifier, true, shape, containerColor, contentColor, 6.dp) { + Box(Modifier.defaultMinSize(56.dp, 56.dp), contentAlignment = Alignment.Center) { content() } + } +} + +@Composable +fun ExtendedFloatingActionButton(text: @Composable () -> Unit, icon: @Composable () -> Unit, onClick: () -> Unit, + modifier: Modifier = Modifier, expanded: Boolean = true, + containerColor: Color = MaterialTheme.colorScheme.primaryContainer, + contentColor: Color = contentColorFor(containerColor)) { + Surface(onClick, modifier, true, MaterialTheme.shapes.large, containerColor, contentColor, 6.dp) { + Row(Modifier.defaultMinSize(80.dp, 56.dp).padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically) { + icon() + if (expanded) { + Spacer(Modifier.width(12.dp)) + ProvideTextStyle(MaterialTheme.typography.labelLarge) { text() } + } + } + } +} + +// --- chips ------------------------------------------------------------------ + +@Composable +fun FilterChip(selected: Boolean, onClick: () -> Unit, label: @Composable () -> Unit, modifier: Modifier = Modifier, + enabled: Boolean = true, leadingIcon: (@Composable () -> Unit)? = null, + trailingIcon: (@Composable () -> Unit)? = null) { + val cs = MaterialTheme.colorScheme + val container = if (selected) cs.secondaryContainer else Color.Transparent + val content = if (selected) cs.onSecondaryContainer else cs.onSurfaceVariant + Surface(onClick, modifier, enabled, MaterialTheme.shapes.small, container, content, + border = if (selected) null else BorderStroke(1.dp, cs.outline)) { + Row(Modifier.height(32.dp).padding(horizontal = 12.dp), verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp)) { + if (leadingIcon != null) Box(Modifier.size(18.dp)) { leadingIcon() } + ProvideTextStyle(MaterialTheme.typography.labelLarge) { label() } + if (trailingIcon != null) Box(Modifier.size(18.dp)) { trailingIcon() } + } + } +} + +@Composable +fun AssistChip(onClick: () -> Unit, label: @Composable () -> Unit, modifier: Modifier = Modifier, + leadingIcon: (@Composable () -> Unit)? = null) = + FilterChip(false, onClick, label, modifier, true, leadingIcon) + +// --- dividers and progress -------------------------------------------------- + +@Composable +fun HorizontalDivider(modifier: Modifier = Modifier, thickness: Dp = 1.dp, + color: Color = MaterialTheme.colorScheme.outlineVariant) { + Box(modifier.fillMaxWidth().height(thickness).background(color)) +} + +@Composable +fun VerticalDivider(modifier: Modifier = Modifier, thickness: Dp = 1.dp, + color: Color = MaterialTheme.colorScheme.outlineVariant) { + Box(modifier.width(thickness).background(color)) +} + +@Composable +fun LinearProgressIndicator(progress: () -> Float, modifier: Modifier = Modifier, + color: Color = MaterialTheme.colorScheme.primary, + trackColor: Color = MaterialTheme.colorScheme.surfaceContainerHighest) { + androidx.compose.foundation.Canvas(modifier.fillMaxWidth().height(4.dp)) { + drawRoundRect(trackColor, cornerRadius = androidx.compose.ui.geometry.CornerRadius(size.height / 2)) + val p = Math.max(0f, Math.min(1f, progress())) + drawRoundRect(color, size = androidx.compose.ui.geometry.Size(size.width * p, size.height), + cornerRadius = androidx.compose.ui.geometry.CornerRadius(size.height / 2)) + } +} + +@Composable +fun CircularProgressIndicator(modifier: Modifier = Modifier, color: Color = MaterialTheme.colorScheme.primary, + strokeWidth: Dp = 4.dp) { + val t = androidx.compose.animation.core.rememberInfiniteTransition() + val angle = t.animateFloat(0f, 360f, androidx.compose.animation.core.infiniteRepeatable( + androidx.compose.animation.core.tween(1100, easing = androidx.compose.animation.core.LinearEasing))) + androidx.compose.foundation.Canvas(modifier.size(40.dp)) { + val w = strokeWidth.toPx() + drawArc(color, angle.value, 270f, false, androidx.compose.ui.geometry.Offset(w / 2, w / 2), + androidx.compose.ui.geometry.Size(size.width - w, size.height - w), + style = androidx.compose.ui.graphics.drawscope.Stroke(w, androidx.compose.ui.graphics.StrokeCap.Round)) + } +} + +@Composable +fun CircularProgressIndicator(progress: () -> Float, modifier: Modifier = Modifier, + color: Color = MaterialTheme.colorScheme.primary, strokeWidth: Dp = 4.dp, + trackColor: Color = MaterialTheme.colorScheme.surfaceContainerHighest) { + androidx.compose.foundation.Canvas(modifier.size(40.dp)) { + val w = strokeWidth.toPx() + val o = androidx.compose.ui.geometry.Offset(w / 2, w / 2) + val s = androidx.compose.ui.geometry.Size(size.width - w, size.height - w) + val st = androidx.compose.ui.graphics.drawscope.Stroke(w, androidx.compose.ui.graphics.StrokeCap.Round) + drawArc(trackColor, 0f, 360f, false, o, s, style = st) + drawArc(color, -90f, 360f * Math.max(0f, Math.min(1f, progress())), false, o, s, style = st) + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Structure.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Structure.kt new file mode 100644 index 00000000000..9d21293e313 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Structure.kt @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material3 + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ComposeNode +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.Immutable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.animation.core.animateColorAsState +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.runtime.getValue +import com.codename1.compose.internal.Fonts +import com.codename1.compose.internal.SafeInsetsElement +import com.codename1.compose.internal.NodeApplier +import com.codename1.compose.internal.TextInputNode + +// --- scaffold --------------------------------------------------------------- + +@Composable +fun Scaffold( + modifier: Modifier = Modifier, + topBar: @Composable () -> Unit = {}, + bottomBar: @Composable () -> Unit = {}, + snackbarHost: @Composable () -> Unit = {}, + floatingActionButton: @Composable () -> Unit = {}, + containerColor: Color = MaterialTheme.colorScheme.background, + contentColor: Color = contentColorFor(containerColor), + content: @Composable (PaddingValues) -> Unit +) { + CompositionLocalProvider(LocalContentColor provides contentColor) { + Column(modifier.fillMaxSize().background(containerColor)) { + topBar() + Box(Modifier.weight(1f).fillMaxWidth()) { + // The bars are laid out above and below the content, not over + // it, so the content needs no padding for them; upstream hands + // their heights and the window insets here because its bars + // overlap the content. A Scaffold with no top bar in a host under + // the status bar therefore starts its content there; the top bar + // is what pads for it. + Box(Modifier.fillMaxSize()) { content(PaddingValues(0.dp)) } + Box(Modifier.align(Alignment.BottomEnd).padding(16.dp)) { floatingActionButton() } + Box(Modifier.align(Alignment.BottomCenter)) { snackbarHost() } + } + bottomBar() + } + } +} + +// --- app bars --------------------------------------------------------------- + +@Immutable +class TopAppBarColors(val containerColor: Color, val titleContentColor: Color, val navigationIconContentColor: Color, + val actionIconContentColor: Color) + +object TopAppBarDefaults { + @Composable + fun topAppBarColors(containerColor: Color = MaterialTheme.colorScheme.surface, + titleContentColor: Color = MaterialTheme.colorScheme.onSurface, + navigationIconContentColor: Color = MaterialTheme.colorScheme.onSurface, + actionIconContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant): TopAppBarColors = + TopAppBarColors(containerColor, titleContentColor, navigationIconContentColor, actionIconContentColor) + + @Composable + fun centerAlignedTopAppBarColors(containerColor: Color = MaterialTheme.colorScheme.surface, + titleContentColor: Color = MaterialTheme.colorScheme.onSurface): TopAppBarColors = + topAppBarColors(containerColor, titleContentColor) +} + +@Composable +fun TopAppBar( + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + navigationIcon: @Composable () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, + colors: TopAppBarColors = TopAppBarDefaults.topAppBarColors() +) = AppBar(title, modifier, navigationIcon, actions, colors, false) + +@Composable +fun CenterAlignedTopAppBar( + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + navigationIcon: @Composable () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, + colors: TopAppBarColors = TopAppBarDefaults.centerAlignedTopAppBarColors() +) = AppBar(title, modifier, navigationIcon, actions, colors, true) + +@Composable +private fun AppBar(title: @Composable () -> Unit, modifier: Modifier, navigationIcon: @Composable () -> Unit, + actions: @Composable RowScope.() -> Unit, colors: TopAppBarColors, centered: Boolean) { + Row(modifier.fillMaxWidth().background(colors.containerColor).then(SafeInsetsElement(top = true, bottom = false)).height(64.dp) + .padding(horizontal = 4.dp), verticalAlignment = Alignment.CenterVertically) { + CompositionLocalProvider(LocalContentColor provides colors.navigationIconContentColor) { navigationIcon() } + Box(Modifier.weight(1f).padding(horizontal = 12.dp), + contentAlignment = if (centered) Alignment.Center else Alignment.CenterStart) { + CompositionLocalProvider(LocalContentColor provides colors.titleContentColor) { + ProvideTextStyle(MaterialTheme.typography.titleLarge) { title() } + } + } + CompositionLocalProvider(LocalContentColor provides colors.actionIconContentColor) { + Row(verticalAlignment = Alignment.CenterVertically, content = actions) + } + } +} + +// --- navigation bar --------------------------------------------------------- + +@Composable +fun NavigationBar(modifier: Modifier = Modifier, containerColor: Color = MaterialTheme.colorScheme.surfaceContainer, + contentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, content: @Composable RowScope.() -> Unit) { + CompositionLocalProvider(LocalContentColor provides contentColor) { + Row(modifier.fillMaxWidth().background(containerColor).then(SafeInsetsElement(top = false, bottom = true)).height(80.dp), + horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically, content = content) + } +} + +@Composable +fun RowScope.NavigationBarItem(selected: Boolean, onClick: () -> Unit, icon: @Composable () -> Unit, + modifier: Modifier = Modifier, enabled: Boolean = true, + label: (@Composable () -> Unit)? = null, alwaysShowLabel: Boolean = true) { + val cs = MaterialTheme.colorScheme + val indicator by animateColorAsState(if (selected) cs.secondaryContainer else cs.secondaryContainer.copy(alpha = 0f)) + Column(modifier.weight(1f).fillMaxSize().clickable(enabled, onClick = onClick), + horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { + Box(Modifier.size(64.dp, 32.dp).background(indicator, CircleShape), contentAlignment = Alignment.Center) { + CompositionLocalProvider(LocalContentColor provides if (selected) cs.onSecondaryContainer else cs.onSurfaceVariant) { + icon() + } + } + if (label != null && (alwaysShowLabel || selected)) { + Spacer(Modifier.height(4.dp)) + CompositionLocalProvider(LocalContentColor provides if (selected) cs.onSurface else cs.onSurfaceVariant) { + ProvideTextStyle(MaterialTheme.typography.labelMedium) { label() } + } + } + } +} + +// --- selection controls ----------------------------------------------------- + +@Composable +fun Switch(checked: Boolean, onCheckedChange: ((Boolean) -> Unit)?, modifier: Modifier = Modifier, enabled: Boolean = true) { + val cs = MaterialTheme.colorScheme + val track by animateColorAsState(if (checked) cs.primary else cs.surfaceContainerHighest) + val thumb by animateColorAsState(if (checked) cs.onPrimary else cs.outline) + val offset by animateDpAsState(if (checked) 24.dp else 4.dp) + val thumbSize by animateDpAsState(if (checked) 24.dp else 16.dp) + var m = modifier.size(52.dp, 32.dp).clip(CircleShape).background(track, CircleShape) + if (!checked) m = m.border(2.dp, cs.outline, CircleShape) + if (onCheckedChange != null) m = m.clickable(enabled) { onCheckedChange(!checked) } + Box(m, contentAlignment = Alignment.CenterStart) { + Box(Modifier.offset(x = offset + (24.dp - thumbSize) / 2).size(thumbSize).background(thumb, CircleShape)) + } +} + +@Composable +fun Checkbox(checked: Boolean, onCheckedChange: ((Boolean) -> Unit)?, modifier: Modifier = Modifier, enabled: Boolean = true) { + val cs = MaterialTheme.colorScheme + var m = modifier.size(40.dp) + if (onCheckedChange != null) m = m.clip(CircleShape).clickable(enabled) { onCheckedChange(!checked) } + Box(m, contentAlignment = Alignment.Center) { + val box = Modifier.size(18.dp).clip(RoundedCornerShape(2.dp)) + if (checked) { + Box(box.background(cs.primary), contentAlignment = Alignment.Center) { + Icon(androidx.compose.ui.graphics.vector.ImageVector.cn1Material("Check", '\ue5ca'), null, + Modifier.size(16.dp), cs.onPrimary) + } + } else { + Box(box.border(2.dp, cs.onSurfaceVariant, RoundedCornerShape(2.dp))) + } + } +} + +// --- text fields ------------------------------------------------------------ + +@Composable +private fun TextInput(value: String, onValueChange: (String) -> Unit, modifier: Modifier, enabled: Boolean, + singleLine: Boolean, hint: String, style: TextStyle, color: Color) { + val font = Fonts.get(Math.max(1, Math.round((if (style.fontSize.isSpecified) style.fontSize.value else 16f) * + com.codename1.compose.internal.Density.density)), style.fontWeight) + ComposeNode( + factory = { TextInputNode() }, + update = { + set(modifier) { this.modifier = it } + set(value) { this.setValue(it) } + set(onValueChange) { this.onValueChange = it } + set(enabled) { this.enabledInput = it } + set(singleLine) { this.singleLine = it } + set(hint) { this.hint = it } + set(color) { this.textColor = it } + set(font) { this.font = it } + } + ) +} + +@Composable +fun BasicTextField(value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + textStyle: TextStyle = TextStyle.Default, singleLine: Boolean = false) { + TextInput(value, onValueChange, modifier, enabled, singleLine, "", textStyle, + if (textStyle.color != Color.Unspecified) textStyle.color else LocalContentColor.current) +} + +@Composable +private fun DecoratedField(value: String, onValueChange: (String) -> Unit, modifier: Modifier, enabled: Boolean, + label: (@Composable () -> Unit)?, placeholder: (@Composable () -> Unit)?, leadingIcon: (@Composable () -> Unit)?, + trailingIcon: (@Composable () -> Unit)?, singleLine: Boolean, shape: Shape, + container: Color, outline: Color?) { + val cs = MaterialTheme.colorScheme + var m = modifier.clip(shape).background(container, shape) + if (outline != null) m = m.border(1.dp, outline, shape) + Row(m.height(if (singleLine) 56.dp else 112.dp).padding(horizontal = 12.dp), verticalAlignment = Alignment.CenterVertically) { + if (leadingIcon != null) { + CompositionLocalProvider(LocalContentColor provides cs.onSurfaceVariant) { leadingIcon() } + Spacer(Modifier.width(12.dp)) + } + Column(Modifier.weight(1f)) { + if (label != null && value.isNotEmpty()) { + CompositionLocalProvider(LocalContentColor provides cs.primary) { + ProvideTextStyle(MaterialTheme.typography.bodySmall) { label() } + } + } + Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterStart) { + TextInput(value, onValueChange, Modifier.fillMaxWidth(), enabled, singleLine, "", + MaterialTheme.typography.bodyLarge, cs.onSurface) + // The placeholder, or the label while there is no value, paints over the + // empty field; text takes no touches, so the field still gets them. + val hint = placeholder ?: label + if (value.isEmpty() && hint != null) { + CompositionLocalProvider(LocalContentColor provides cs.onSurfaceVariant) { + ProvideTextStyle(MaterialTheme.typography.bodyLarge) { hint() } + } + } + } + } + if (trailingIcon != null) { + Spacer(Modifier.width(12.dp)) + CompositionLocalProvider(LocalContentColor provides cs.onSurfaceVariant) { trailingIcon() } + } + } +} + +/// A filled text field. The placeholder (or the label) shows while the field is empty. +/// Editing is the platform's own: the field is a Codename One `TextField`. +@Composable +fun TextField(value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + label: (@Composable () -> Unit)? = null, placeholder: (@Composable () -> Unit)? = null, + leadingIcon: (@Composable () -> Unit)? = null, trailingIcon: (@Composable () -> Unit)? = null, + singleLine: Boolean = false, shape: Shape = RoundedCornerShape(topStart = 4.dp, topEnd = 4.dp)) { + DecoratedField(value, onValueChange, modifier, enabled, label, placeholder, leadingIcon, trailingIcon, singleLine, + shape, MaterialTheme.colorScheme.surfaceContainerHighest, null) +} + +@Composable +fun OutlinedTextField(value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + label: (@Composable () -> Unit)? = null, placeholder: (@Composable () -> Unit)? = null, + leadingIcon: (@Composable () -> Unit)? = null, trailingIcon: (@Composable () -> Unit)? = null, + singleLine: Boolean = false, shape: Shape = RoundedCornerShape(4.dp)) { + DecoratedField(value, onValueChange, modifier, enabled, label, placeholder, leadingIcon, trailingIcon, singleLine, + shape, Color.Transparent, MaterialTheme.colorScheme.outline) +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Theme.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Theme.kt new file mode 100644 index 00000000000..6becd25c2bb --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Theme.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material3 + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp + +/// The Material 3 shape scale. +@Immutable +data class Shapes( + val extraSmall: RoundedCornerShape = RoundedCornerShape(4.dp), + val small: RoundedCornerShape = RoundedCornerShape(8.dp), + val medium: RoundedCornerShape = RoundedCornerShape(12.dp), + val large: RoundedCornerShape = RoundedCornerShape(16.dp), + val extraLarge: RoundedCornerShape = RoundedCornerShape(28.dp) +) + +internal val LocalColorScheme = staticCompositionLocalOf { lightColorScheme() } +internal val LocalTypography = staticCompositionLocalOf { Typography() } +internal val LocalShapes = staticCompositionLocalOf { Shapes() } + +/// The color content is drawn in: text and icons read it when no color is given. +val LocalContentColor = compositionLocalOf { Color.Black } + +/// The style [Text] starts from. +val LocalTextStyle = compositionLocalOf { TextStyle.Default } + +@Composable +fun MaterialTheme( + colorScheme: ColorScheme = MaterialTheme.colorScheme, + shapes: Shapes = MaterialTheme.shapes, + typography: Typography = MaterialTheme.typography, + content: @Composable () -> Unit +) { + CompositionLocalProvider( + LocalColorScheme provides colorScheme, + LocalTypography provides typography, + LocalShapes provides shapes, + LocalContentColor provides colorScheme.onSurface, + LocalTextStyle provides typography.bodyLarge + ) { + content() + } +} + +object MaterialTheme { + val colorScheme: ColorScheme + @Composable @ReadOnlyComposable get() = LocalColorScheme.current + val typography: Typography + @Composable @ReadOnlyComposable get() = LocalTypography.current + val shapes: Shapes + @Composable @ReadOnlyComposable get() = LocalShapes.current +} + +@Composable +fun ProvideTextStyle(value: TextStyle, content: @Composable () -> Unit) { + val merged = LocalTextStyle.current.merge(value) + CompositionLocalProvider(LocalTextStyle provides merged, content = content) +} + +@Composable +@ReadOnlyComposable +fun contentColorFor(backgroundColor: Color): Color { + val c = MaterialTheme.colorScheme.contentColorFor(backgroundColor) + return if (c == Color.Unspecified) LocalContentColor.current else c +} + +internal val CornerFull = RoundedCornerShape(CornerSize(50)) diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Typography.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Typography.kt new file mode 100644 index 00000000000..446c824a7fd --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/material3/Typography.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.material3 + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +/// The Material 3 type scale; the defaults are the baseline sizes. +@Immutable +data class Typography( + val displayLarge: TextStyle = TextStyle(fontSize = 57.sp, lineHeight = 64.sp, fontWeight = FontWeight(400)), + val displayMedium: TextStyle = TextStyle(fontSize = 45.sp, lineHeight = 52.sp, fontWeight = FontWeight(400)), + val displaySmall: TextStyle = TextStyle(fontSize = 36.sp, lineHeight = 44.sp, fontWeight = FontWeight(400)), + val headlineLarge: TextStyle = TextStyle(fontSize = 32.sp, lineHeight = 40.sp, fontWeight = FontWeight(400)), + val headlineMedium: TextStyle = TextStyle(fontSize = 28.sp, lineHeight = 36.sp, fontWeight = FontWeight(400)), + val headlineSmall: TextStyle = TextStyle(fontSize = 24.sp, lineHeight = 32.sp, fontWeight = FontWeight(400)), + val titleLarge: TextStyle = TextStyle(fontSize = 22.sp, lineHeight = 28.sp, fontWeight = FontWeight(400)), + val titleMedium: TextStyle = TextStyle(fontSize = 16.sp, lineHeight = 24.sp, fontWeight = FontWeight(500)), + val titleSmall: TextStyle = TextStyle(fontSize = 14.sp, lineHeight = 20.sp, fontWeight = FontWeight(500)), + val bodyLarge: TextStyle = TextStyle(fontSize = 16.sp, lineHeight = 24.sp, fontWeight = FontWeight(400)), + val bodyMedium: TextStyle = TextStyle(fontSize = 14.sp, lineHeight = 20.sp, fontWeight = FontWeight(400)), + val bodySmall: TextStyle = TextStyle(fontSize = 12.sp, lineHeight = 16.sp, fontWeight = FontWeight(400)), + val labelLarge: TextStyle = TextStyle(fontSize = 14.sp, lineHeight = 20.sp, fontWeight = FontWeight(500)), + val labelMedium: TextStyle = TextStyle(fontSize = 12.sp, lineHeight = 16.sp, fontWeight = FontWeight(500)), + val labelSmall: TextStyle = TextStyle(fontSize = 11.sp, lineHeight = 16.sp, fontWeight = FontWeight(500)) +) diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/Alignment.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/Alignment.kt new file mode 100644 index 00000000000..e8048675804 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/Alignment.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.LayoutDirection + +/// Where a child of a given size goes in the space it has. +@Immutable +fun interface Alignment { + fun align(size: IntSize, space: IntSize, layoutDirection: LayoutDirection): IntOffset + + @Immutable + fun interface Horizontal { + fun align(size: Int, space: Int, layoutDirection: LayoutDirection): Int + } + + @Immutable + fun interface Vertical { + fun align(size: Int, space: Int): Int + } + + companion object { + val TopStart: Alignment = BiasAlignment(-1f, -1f) + val TopCenter: Alignment = BiasAlignment(0f, -1f) + val TopEnd: Alignment = BiasAlignment(1f, -1f) + val CenterStart: Alignment = BiasAlignment(-1f, 0f) + val Center: Alignment = BiasAlignment(0f, 0f) + val CenterEnd: Alignment = BiasAlignment(1f, 0f) + val BottomStart: Alignment = BiasAlignment(-1f, 1f) + val BottomCenter: Alignment = BiasAlignment(0f, 1f) + val BottomEnd: Alignment = BiasAlignment(1f, 1f) + + val Top: Vertical = BiasAlignment.Vertical(-1f) + val CenterVertically: Vertical = BiasAlignment.Vertical(0f) + val Bottom: Vertical = BiasAlignment.Vertical(1f) + + val Start: Horizontal = BiasAlignment.Horizontal(-1f) + val CenterHorizontally: Horizontal = BiasAlignment.Horizontal(0f) + val End: Horizontal = BiasAlignment.Horizontal(1f) + } +} + +/// An alignment expressed as a bias: -1 is the start or top, 0 the center, 1 the +/// end or bottom. +@Immutable +data class BiasAlignment(val horizontalBias: Float, val verticalBias: Float) : Alignment { + override fun align(size: IntSize, space: IntSize, layoutDirection: LayoutDirection): IntOffset { + val h = Horizontal(horizontalBias).align(size.width, space.width, layoutDirection) + val v = Vertical(verticalBias).align(size.height, space.height) + return IntOffset(h, v) + } + + @Immutable + data class Horizontal(val bias: Float) : Alignment.Horizontal { + override fun align(size: Int, space: Int, layoutDirection: LayoutDirection): Int { + val b = if (layoutDirection == LayoutDirection.Ltr) bias else -bias + return Math.round((space - size) / 2f * (1 + b)) + } + } + + @Immutable + data class Vertical(val bias: Float) : Alignment.Vertical { + override fun align(size: Int, space: Int): Int = Math.round((space - size) / 2f * (1 + bias)) + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/Modifier.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/Modifier.kt new file mode 100644 index 00000000000..61ca8377ed4 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/Modifier.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui + +import androidx.compose.runtime.Stable + +/// An ordered chain of elements that decorate or configure a layout: sizes, +/// padding, backgrounds, click handling. Elements apply outermost first, so +/// `Modifier.padding(8.dp).background(c)` paints the background inside the +/// padding and `Modifier.background(c).padding(8.dp)` paints it under it. +@Stable +interface Modifier { + fun foldIn(initial: R, operation: (R, Element) -> R): R + fun foldOut(initial: R, operation: (Element, R) -> R): R + fun any(predicate: (Element) -> Boolean): Boolean + fun all(predicate: (Element) -> Boolean): Boolean + + infix fun then(other: Modifier): Modifier = + if (other === Modifier) this else CombinedModifier(this, other) + + interface Element : Modifier { + override fun foldIn(initial: R, operation: (R, Element) -> R): R = operation(initial, this) + override fun foldOut(initial: R, operation: (Element, R) -> R): R = operation(this, initial) + override fun any(predicate: (Element) -> Boolean): Boolean = predicate(this) + override fun all(predicate: (Element) -> Boolean): Boolean = predicate(this) + } + + /// The empty modifier, and the receiver every chain starts from. + companion object : Modifier { + override fun foldIn(initial: R, operation: (R, Element) -> R): R = initial + override fun foldOut(initial: R, operation: (Element, R) -> R): R = initial + override fun any(predicate: (Element) -> Boolean): Boolean = false + override fun all(predicate: (Element) -> Boolean): Boolean = true + override infix fun then(other: Modifier): Modifier = other + override fun toString(): String = "Modifier" + } +} + +class CombinedModifier(internal val outer: Modifier, internal val inner: Modifier) : Modifier { + override fun foldIn(initial: R, operation: (R, Modifier.Element) -> R): R = + inner.foldIn(outer.foldIn(initial, operation), operation) + + override fun foldOut(initial: R, operation: (Modifier.Element, R) -> R): R = + outer.foldOut(inner.foldOut(initial, operation), operation) + + override fun any(predicate: (Modifier.Element) -> Boolean): Boolean = outer.any(predicate) || inner.any(predicate) + override fun all(predicate: (Modifier.Element) -> Boolean): Boolean = outer.all(predicate) && inner.all(predicate) + + override fun equals(other: Any?): Boolean = + other is CombinedModifier && outer == other.outer && inner == other.inner + + override fun hashCode(): Int = outer.hashCode() + 31 * inner.hashCode() + + override fun toString(): String = "[" + foldIn("") { acc, e -> if (acc.isEmpty()) e.toString() else "$acc, $e" } + "]" +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/draw/Draw.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/draw/Draw.kt new file mode 100644 index 00000000000..5e1c86aef9f --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/draw/Draw.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.draw + +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawscope.DrawScope +import com.codename1.compose.internal.AlphaElement +import com.codename1.compose.internal.ClipElement +import com.codename1.compose.internal.DrawBehindElement + +fun Modifier.clip(shape: Shape): Modifier = this.then(ClipElement(shape)) +fun Modifier.alpha(alpha: Float): Modifier = this.then(AlphaElement(alpha)) +fun Modifier.drawBehind(onDraw: DrawScope.() -> Unit): Modifier = this.then(DrawBehindElement(onDraw)) diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/geometry/Geometry.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/geometry/Geometry.kt new file mode 100644 index 00000000000..42e2d9e8f25 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/geometry/Geometry.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.geometry + +/// A point in pixels, relative to the drawing area's top left corner. +data class Offset(val x: Float, val y: Float) { + operator fun plus(other: Offset): Offset = Offset(x + other.x, y + other.y) + operator fun minus(other: Offset): Offset = Offset(x - other.x, y - other.y) + operator fun times(operand: Float): Offset = Offset(x * operand, y * operand) + operator fun div(operand: Float): Offset = Offset(x / operand, y / operand) + operator fun unaryMinus(): Offset = Offset(-x, -y) + + companion object { + val Zero: Offset = Offset(0f, 0f) + } +} + +/// A width and a height in pixels. +data class Size(val width: Float, val height: Float) { + val minDimension: Float get() = Math.min(width, height) + val maxDimension: Float get() = Math.max(width, height) + val center: Offset get() = Offset(width / 2f, height / 2f) + operator fun times(operand: Float): Size = Size(width * operand, height * operand) + operator fun div(operand: Float): Size = Size(width / operand, height / operand) + + companion object { + val Zero: Size = Size(0f, 0f) + } +} + +/// A rectangle in pixels. +data class Rect(val left: Float, val top: Float, val right: Float, val bottom: Float) { + constructor(offset: Offset, size: Size) : this(offset.x, offset.y, offset.x + size.width, offset.y + size.height) + + val width: Float get() = right - left + val height: Float get() = bottom - top + val size: Size get() = Size(width, height) + val topLeft: Offset get() = Offset(left, top) + val center: Offset get() = Offset(left + width / 2f, top + height / 2f) +} + +/// A corner radius in pixels. +data class CornerRadius(val x: Float, val y: Float = x) { + companion object { + val Zero: CornerRadius = CornerRadius(0f, 0f) + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/Graphics.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/Graphics.kt new file mode 100644 index 00000000000..7bebd1e0539 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/Graphics.kt @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.graphics + +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection + +/// An sRGB color. The value is packed as Compose packs an sRGB color: the ARGB +/// integer in the upper 32 bits. +@JvmInline +value class Color(val value: ULong) { + val alpha: Float get() = ((argb ushr 24) and 0xff) / 255f + val red: Float get() = ((argb ushr 16) and 0xff) / 255f + val green: Float get() = ((argb ushr 8) and 0xff) / 255f + val blue: Float get() = (argb and 0xff) / 255f + + internal val argb: Int get() = (value shr 32).toInt() + + fun copy(alpha: Float = this.alpha, red: Float = this.red, green: Float = this.green, blue: Float = this.blue): Color = + Color(red, green, blue, alpha) + + override fun toString(): String = + if (this == Unspecified) "Color.Unspecified" else "Color(#" + Integer.toHexString(argb) + ")" + + companion object { + val Black: Color = Color(0xFF000000) + val DarkGray: Color = Color(0xFF444444) + val Gray: Color = Color(0xFF888888) + val LightGray: Color = Color(0xFFCCCCCC) + val White: Color = Color(0xFFFFFFFF) + val Red: Color = Color(0xFFFF0000) + val Green: Color = Color(0xFF00FF00) + val Blue: Color = Color(0xFF0000FF) + val Yellow: Color = Color(0xFFFFFF00) + val Cyan: Color = Color(0xFF00FFFF) + val Magenta: Color = Color(0xFFFF00FF) + val Transparent: Color = Color(0x00000000) + + /// Distinct from every real color: the default of a parameter that means + /// "use the theme's". + val Unspecified: Color = Color(0x10UL) + } +} + +fun Color(color: Long): Color = Color(((color and 0xffffffffL).toULong()) shl 32) + +fun Color(color: Int): Color = Color((color.toLong() and 0xffffffffL).toULong() shl 32) + +fun Color(red: Float, green: Float, blue: Float, alpha: Float = 1f): Color { + fun c(v: Float): Int = Math.round(Math.max(0f, Math.min(1f, v)) * 255f) + return Color((c(alpha) shl 24) or (c(red) shl 16) or (c(green) shl 8) or c(blue)) +} + +fun Color(red: Int, green: Int, blue: Int, alpha: Int = 0xff): Color = + Color(((alpha and 0xff) shl 24) or ((red and 0xff) shl 16) or ((green and 0xff) shl 8) or (blue and 0xff)) + +val Color.isSpecified: Boolean get() = this != Color.Unspecified +val Color.isUnspecified: Boolean get() = this == Color.Unspecified + +inline fun Color.takeOrElse(block: () -> Color): Color = if (isSpecified) this else block() + +fun Color.toArgb(): Int = argb + +fun lerp(start: Color, stop: Color, fraction: Float): Color { + fun mix(a: Float, b: Float): Float = a + (b - a) * fraction + return Color(mix(start.red, stop.red), mix(start.green, stop.green), mix(start.blue, stop.blue), + mix(start.alpha, stop.alpha)) +} + +/// What fills a shape: one color, or a gradient sampled along an axis. +sealed class Brush { + companion object { + fun verticalGradient(colors: List, startY: Float = 0f, endY: Float = Float.POSITIVE_INFINITY): Brush = + LinearGradient(colors, true) + + fun horizontalGradient(colors: List, startX: Float = 0f, endX: Float = Float.POSITIVE_INFINITY): Brush = + LinearGradient(colors, false) + + fun linearGradient(colors: List): Brush = LinearGradient(colors, true) + } +} + +class SolidColor(val value: Color) : Brush() + +internal class LinearGradient(val colors: List, val vertical: Boolean) : Brush() + +/// The outline a shape resolves to at a given size. +sealed class Outline { + class Rectangle(val width: Float, val height: Float) : Outline() + + /// A rounded rectangle; each radius is in pixels. + class Rounded( + val width: Float, val height: Float, + val topLeft: Float, val topRight: Float, val bottomRight: Float, val bottomLeft: Float + ) : Outline() +} + +interface Shape { + fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline +} + +val RectangleShape: Shape = object : Shape { + override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline = + Outline.Rectangle(size.width, size.height) + + override fun toString(): String = "RectangleShape" +} + +/// A decoded bitmap. Wraps a Codename One image. +class ImageBitmap internal constructor(internal val image: com.codename1.ui.Image) { + val width: Int get() = image.width + val height: Int get() = image.height + + companion object { + /// Wraps an image the application already has, for [androidx.compose.foundation.Image]. + fun fromCodenameOne(image: com.codename1.ui.Image): ImageBitmap = ImageBitmap(image) + } +} + +fun com.codename1.ui.Image.asImageBitmap(): ImageBitmap = ImageBitmap(this) + +enum class StrokeCap { Butt, Round, Square } + +object DefaultAlpha { + const val value: Float = 1f +} + +/// How a drawing combines with what is under it. Only normal source-over drawing +/// is supported; the other modes draw as [SrcOver]. +enum class BlendMode { SrcOver, Src, Multiply, Screen } + +class ColorFilter private constructor(internal val tint: Color) { + companion object { + fun tint(color: Color, blendMode: BlendMode = BlendMode.SrcOver): ColorFilter = ColorFilter(color) + } +} + +internal fun Offset.isFinite(): Boolean = !x.isInfinite() && !y.isInfinite() diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt new file mode 100644 index 00000000000..501c64ebdde --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.graphics.drawscope + +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection + +/// How a shape is drawn: filled, or stroked with a width in pixels. +sealed class DrawStyle + +object Fill : DrawStyle() + +class Stroke(val width: Float = 0f, val cap: StrokeCap = StrokeCap.Butt) : DrawStyle() + +/// The surface a [androidx.compose.foundation.Canvas] or `drawBehind` draws on. +/// Coordinates are pixels, from the top left corner of the drawing's box. +interface DrawScope : Density { + val size: Size + val layoutDirection: LayoutDirection get() = LayoutDirection.Ltr + val center: Offset get() = Offset(size.width / 2f, size.height / 2f) + + fun drawRect(color: Color, topLeft: Offset = Offset.Zero, size: Size = this.size, alpha: Float = 1f, + style: DrawStyle = Fill) + + fun drawRoundRect(color: Color, topLeft: Offset = Offset.Zero, size: Size = this.size, + cornerRadius: CornerRadius = CornerRadius.Zero, alpha: Float = 1f, style: DrawStyle = Fill) + + fun drawCircle(color: Color, radius: Float = size.minDimension / 2f, center: Offset = this.center, + alpha: Float = 1f, style: DrawStyle = Fill) + + fun drawArc(color: Color, startAngle: Float, sweepAngle: Float, useCenter: Boolean, + topLeft: Offset = Offset.Zero, size: Size = this.size, alpha: Float = 1f, style: DrawStyle = Fill) + + fun drawLine(color: Color, start: Offset, end: Offset, strokeWidth: Float = 0f, + cap: StrokeCap = StrokeCap.Butt, alpha: Float = 1f) + + fun drawImage(image: ImageBitmap, topLeft: Offset = Offset.Zero, alpha: Float = 1f) + + /// Draws `points` joined by straight segments. + fun drawPolyline(color: Color, points: List, strokeWidth: Float, alpha: Float = 1f) + + /// Fills the polygon `points` describe. + fun drawPolygon(color: Color, points: List, alpha: Float = 1f) + + fun translate(left: Float, top: Float, block: DrawScope.() -> Unit) +} + +fun DrawScope.inset(horizontal: Float, vertical: Float, block: DrawScope.() -> Unit) { + translate(horizontal, vertical, block) +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt new file mode 100644 index 00000000000..7b820145851 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.graphics.vector + +import androidx.compose.runtime.Immutable + +/// A vector icon. The icons shipped here are glyphs of the material design font +/// every Codename One application already carries, drawn at any size. +@Immutable +class ImageVector internal constructor(val name: String, internal val glyph: Char) { + override fun equals(other: Any?): Boolean = other is ImageVector && other.glyph == glyph + + override fun hashCode(): Int = glyph.code + + override fun toString(): String = "ImageVector($name)" + + companion object { + /// An icon from any glyph of the material design font. + fun cn1Material(name: String, glyph: Char): ImageVector = ImageVector(name, glyph) + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/layout/ContentScale.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/layout/ContentScale.kt new file mode 100644 index 00000000000..70ff46358f4 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/layout/ContentScale.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.layout + +/// How an image is scaled into the box it is given. +class ContentScale private constructor(internal val name: String) { + override fun toString(): String = "ContentScale.$name" + + companion object { + val Fit: ContentScale = ContentScale("Fit") + val Crop: ContentScale = ContentScale("Crop") + val FillBounds: ContentScale = ContentScale("FillBounds") + val FillWidth: ContentScale = ContentScale("FillWidth") + val Inside: ContentScale = ContentScale("Inside") + val None: ContentScale = ContentScale("None") + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/platform/Platform.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/platform/Platform.kt new file mode 100644 index 00000000000..0b67bac43ad --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/platform/Platform.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.platform + +import androidx.compose.ui.Modifier +import com.codename1.compose.internal.TestTagElement + +/// Names the node, for tests: the Codename One component's name is the tag. +fun Modifier.testTag(tag: String): Modifier = this.then(TestTagElement(tag)) diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/TextStyle.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/TextStyle.kt new file mode 100644 index 00000000000..9b81453e08c --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/TextStyle.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.text + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.TextUnit + +/// How text is drawn. Unspecified values fall back to the style it is merged +/// into, as in Compose. +@Immutable +data class TextStyle( + val color: Color = Color.Unspecified, + val fontSize: TextUnit = TextUnit.Unspecified, + val fontWeight: FontWeight? = null, + val fontStyle: FontStyle? = null, + val fontFamily: FontFamily? = null, + val letterSpacing: TextUnit = TextUnit.Unspecified, + val textDecoration: TextDecoration? = null, + val textAlign: TextAlign = TextAlign.Unspecified, + val lineHeight: TextUnit = TextUnit.Unspecified +) { + fun merge(other: TextStyle?): TextStyle { + if (other == null) return this + return TextStyle( + color = if (other.color != Color.Unspecified) other.color else color, + fontSize = if (other.fontSize.isSpecified) other.fontSize else fontSize, + fontWeight = other.fontWeight ?: fontWeight, + fontStyle = other.fontStyle ?: fontStyle, + fontFamily = other.fontFamily ?: fontFamily, + letterSpacing = if (other.letterSpacing.isSpecified) other.letterSpacing else letterSpacing, + textDecoration = other.textDecoration ?: textDecoration, + textAlign = if (other.textAlign != TextAlign.Unspecified) other.textAlign else textAlign, + lineHeight = if (other.lineHeight.isSpecified) other.lineHeight else lineHeight + ) + } + + operator fun plus(other: TextStyle): TextStyle = merge(other) + + companion object { + val Default: TextStyle = TextStyle() + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/font/FontWeight.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/font/FontWeight.kt new file mode 100644 index 00000000000..002543c7785 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/font/FontWeight.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.text.font + +import androidx.compose.runtime.Immutable + +@Immutable +data class FontWeight(val weight: Int) : Comparable { + override fun compareTo(other: FontWeight): Int = weight.compareTo(other.weight) + + companion object { + val Thin: FontWeight = FontWeight(100) + val ExtraLight: FontWeight = FontWeight(200) + val Light: FontWeight = FontWeight(300) + val Normal: FontWeight = FontWeight(400) + val Medium: FontWeight = FontWeight(500) + val SemiBold: FontWeight = FontWeight(600) + val Bold: FontWeight = FontWeight(700) + val ExtraBold: FontWeight = FontWeight(800) + val Black: FontWeight = FontWeight(900) + } +} + +@JvmInline +value class FontStyle(val value: Int) { + companion object { + val Normal: FontStyle = FontStyle(0) + val Italic: FontStyle = FontStyle(1) + } +} + +/// A font family. Only the platform's own families exist here: text is drawn +/// with the device's native font. +@Immutable +class FontFamily private constructor(internal val name: String) { + override fun toString(): String = "FontFamily.$name" + + companion object { + val Default: FontFamily = FontFamily("Default") + val SansSerif: FontFamily = FontFamily("SansSerif") + val Serif: FontFamily = FontFamily("Serif") + val Monospace: FontFamily = FontFamily("Monospace") + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/style/TextStyles.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/style/TextStyles.kt new file mode 100644 index 00000000000..7cf2c5c1e3d --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/text/style/TextStyles.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.text.style + +@JvmInline +value class TextAlign(val value: Int) { + companion object { + val Left: TextAlign = TextAlign(1) + val Right: TextAlign = TextAlign(2) + val Center: TextAlign = TextAlign(3) + val Justify: TextAlign = TextAlign(4) + val Start: TextAlign = TextAlign(5) + val End: TextAlign = TextAlign(6) + val Unspecified: TextAlign = TextAlign(0) + } +} + +@JvmInline +value class TextOverflow(val value: Int) { + companion object { + val Clip: TextOverflow = TextOverflow(1) + val Ellipsis: TextOverflow = TextOverflow(2) + val Visible: TextOverflow = TextOverflow(3) + } +} + +@JvmInline +value class TextDecoration(val mask: Int) { + companion object { + val None: TextDecoration = TextDecoration(0) + val Underline: TextDecoration = TextDecoration(1) + val LineThrough: TextDecoration = TextDecoration(2) + } +} diff --git a/maven/compose-compat/src/main/kotlin/androidx/compose/ui/unit/Units.kt b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/unit/Units.kt new file mode 100644 index 00000000000..a675221bf20 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/androidx/compose/ui/unit/Units.kt @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package androidx.compose.ui.unit + +import com.codename1.compose.internal.Density as Cn1Density + +/// A density-independent length: 1/160 of an inch, the unit every size in a +/// Compose layout is written in. +@JvmInline +value class Dp(val value: Float) : Comparable { + operator fun plus(other: Dp): Dp = Dp(value + other.value) + operator fun minus(other: Dp): Dp = Dp(value - other.value) + operator fun unaryMinus(): Dp = Dp(-value) + operator fun div(other: Float): Dp = Dp(value / other) + operator fun div(other: Int): Dp = Dp(value / other) + operator fun div(other: Dp): Float = value / other.value + operator fun times(other: Float): Dp = Dp(value * other) + operator fun times(other: Int): Dp = Dp(value * other) + override fun compareTo(other: Dp): Int = value.compareTo(other.value) + override fun toString(): String = if (value.isNaN()) "Dp.Unspecified" else "$value.dp" + + companion object { + val Hairline: Dp = Dp(0f) + val Infinity: Dp = Dp(Float.POSITIVE_INFINITY) + val Unspecified: Dp = Dp(Float.NaN) + } +} + +inline val Int.dp: Dp get() = Dp(this.toFloat()) +inline val Float.dp: Dp get() = Dp(this) +inline val Double.dp: Dp get() = Dp(this.toFloat()) + +operator fun Int.times(other: Dp): Dp = Dp(this * other.value) +operator fun Float.times(other: Dp): Dp = Dp(this * other.value) + +fun min(a: Dp, b: Dp): Dp = if (a.value <= b.value) a else b +fun max(a: Dp, b: Dp): Dp = if (a.value >= b.value) a else b + +val Dp.isSpecified: Boolean get() = !value.isNaN() +val Dp.isUnspecified: Boolean get() = value.isNaN() + +/// A width and a height in [Dp]. +data class DpSize(val width: Dp, val height: Dp) + +/// A text size. Only scaled pixels are supported; `em` is treated as a multiple of +/// the default body size. +@JvmInline +value class TextUnit internal constructor(internal val packed: Long) { + val value: Float get() = Float.fromBits((packed and 0xffffffffL).toInt()) + val isSp: Boolean get() = (packed ushr 32) == 1L + val isEm: Boolean get() = (packed ushr 32) == 2L + val isUnspecified: Boolean get() = (packed ushr 32) == 0L + val isSpecified: Boolean get() = !isUnspecified + operator fun times(other: Float): TextUnit = textUnit(packed ushr 32, value * other) + override fun toString(): String = when { + isSp -> "$value.sp" + isEm -> "$value.em" + else -> "Unspecified" + } + + companion object { + val Unspecified: TextUnit = TextUnit(0L) + } +} + +internal fun textUnit(type: Long, value: Float): TextUnit = + TextUnit((type shl 32) or (value.toRawBits().toLong() and 0xffffffffL)) + +val Int.sp: TextUnit get() = textUnit(1L, this.toFloat()) +val Float.sp: TextUnit get() = textUnit(1L, this) +val Double.sp: TextUnit get() = textUnit(1L, this.toFloat()) +val Int.em: TextUnit get() = textUnit(2L, this.toFloat()) +val Float.em: TextUnit get() = textUnit(2L, this) +val Double.em: TextUnit get() = textUnit(2L, this.toFloat()) + +/// A size in pixels. +data class IntSize(val width: Int, val height: Int) { + companion object { + val Zero: IntSize = IntSize(0, 0) + } +} + +/// A position in pixels. +data class IntOffset(val x: Int, val y: Int) { + companion object { + val Zero: IntOffset = IntOffset(0, 0) + } +} + +enum class LayoutDirection { Ltr, Rtl } + +/// Converts between [Dp] and pixels on the current display. +interface Density { + val density: Float + val fontScale: Float + fun Dp.toPx(): Float = value * density + fun Dp.roundToPx(): Int = Math.round(toPx()) + fun TextUnit.toPx(): Float = value * density * fontScale + fun Int.toDp(): Dp = Dp(this / density) + fun Float.toDp(): Dp = Dp(this / density) +} + +fun Density(density: Float, fontScale: Float = 1f): Density = object : Density { + override val density: Float = density + override val fontScale: Float = fontScale +} + +/// The display's density, for code that converts by hand. +val currentDisplayDensity: Density get() = Cn1Density.current diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/Compose.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/Compose.kt new file mode 100644 index 00000000000..6bd73cf9acf --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/Compose.kt @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose + +import androidx.compose.runtime.Composable +import com.codename1.compose.internal.ComposeHost +import com.codename1.ui.Container +import com.codename1.ui.Form +import com.codename1.ui.layouts.BorderLayout + +/// Embeds Compose content in a Codename One user interface. The content's nodes +/// are real Codename One components, so a composition can sit beside any other +/// component, and recomposes on the EDT. +/// +/// ```kotlin +/// val view = ComposeView { App() } +/// form.add(BorderLayout.CENTER, view) +/// ``` +class ComposeView(content: @Composable () -> Unit) : Container(BorderLayout()) { + private val host = ComposeHost() + + init { + allStyles.setPadding(0, 0, 0, 0) + allStyles.setMargin(0, 0, 0, 0) + add(BorderLayout.CENTER, host) + host.setContent(content) + } + + /// Replaces the content. + fun setContent(content: @Composable () -> Unit) { + host.setContent(content) + } + + /// Ends the composition: its effects are cancelled and its nodes removed. + fun dispose() { + host.dispose() + } + + /// Whether a recomposition or an animation frame is still pending; tests wait + /// for this to become false. + fun hasPendingWork(): Boolean = host.cn1HasPendingWork() + + /// Delivers an animation frame now, for tests that do not wait for the EDT's + /// animation loop. + fun cn1Frame() { + host.sendFrame() + } +} + +/// A form whose whole content is a composition: the usual way to start a +/// Compose application on Codename One. +/// +/// ```kotlin +/// override fun runApp() { +/// ComposeForm { App() }.show() +/// } +/// ``` +class ComposeForm(title: String = "", content: @Composable () -> Unit) : Form(title, BorderLayout()) { + val view: ComposeView = ComposeView(content) + + init { + contentPane.allStyles.setPadding(0, 0, 0, 0) + if (title.isEmpty()) { + toolbar?.isHidden = true + titleArea.isVisible = false + } + add(BorderLayout.CENTER, view) + } +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt new file mode 100644 index 00000000000..0ddb3893145 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.runtime.AbstractApplier +import androidx.compose.runtime.BroadcastFrameClock +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Composition +import androidx.compose.runtime.Recomposer +import com.codename1.ui.Component +import com.codename1.ui.Container +import com.codename1.ui.Form +import com.codename1.ui.geom.Dimension +import com.codename1.ui.layouts.Layout +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch + +/// Inserts, moves and removes the components a composition emits. +internal class NodeApplier(root: Container) : AbstractApplier(root) { + override fun insertTopDown(index: Int, instance: Component) { + // Children are attached bottom up, so a parent is complete when it is added. + } + + override fun insertBottomUp(index: Int, instance: Component) { + val parent = current as? Container ?: return + if (index >= parent.componentCount) { + parent.addComponent(instance) + } else { + parent.addComponent(index, instance) + } + invalidate(parent) + } + + override fun remove(index: Int, count: Int) { + val parent = current as? Container ?: return + for (i in 0 until count) { + parent.removeComponent(parent.getComponentAt(index)) + } + invalidate(parent) + } + + override fun move(from: Int, to: Int, count: Int) { + val parent = current as? Container ?: return + val moved = ArrayList(count) + for (i in 0 until count) { + moved.add(parent.getComponentAt(from)) + parent.removeComponent(parent.getComponentAt(from)) + } + val dest = if (from > to) to else to - count + for (i in 0 until count) { + parent.addComponent(dest + i, moved[i]) + } + invalidate(parent) + } + + override fun onClear() { + (root as? Container)?.removeAll() + } + + override fun onEndChanges() { + val r = root + if (r is ComposeHost) { + r.requestRemeasure() + } + } + + private fun invalidate(c: Container) { + if (c is UiNode) { + c.invalidateMeasure() + } + } +} + +/// The Codename One component a composition lives in. Its children are the +/// composition's top level nodes, stacked as a `Box` would stack them. +internal class ComposeHost : Container(HostLayout) { + private val clock = BroadcastFrameClock { scheduleFrame() } + private val scope = CoroutineScope(EdtDispatcher.Main + clock + Job()) + private val recomposer = Recomposer(scope.coroutineContext) + private var composition: Composition? = null + private var animatedOn: Form? = null + private var frameRequested = false + private var timerPending = false + + init { + cn1StripStyle(this) + GlobalSnapshotManager.ensureStarted() + scope.launch(start = CoroutineStart.UNDISPATCHED) { + recomposer.runRecomposeAndApplyChanges() + } + } + + fun setContent(content: @Composable () -> Unit) { + val c = composition ?: Composition(NodeApplier(this), recomposer).also { composition = it } + c.setContent(content) + requestRemeasure() + } + + fun dispose() { + composition?.dispose() + composition = null + recomposer.cancel() + scope.cancel() + animatedOn?.deregisterAnimated(this) + animatedOn = null + } + + /// Whether a composition change, a state change or a frame is pending. + fun cn1HasPendingWork(): Boolean = recomposer.hasPendingWork || clock.hasAwaiters + + // --- frames ------------------------------------------------------------- + + private fun scheduleFrame() { + frameRequested = true + val f = componentForm + if (f != null && f.isVisible) { + if (animatedOn !== f) { + animatedOn?.deregisterAnimated(this) + f.registerAnimated(this) + animatedOn = f + } + return + } + if (!timerPending) { + timerPending = true + com.codename1.ui.CN.setTimeout(16) { + timerPending = false + sendFrame() + } + } + } + + internal fun sendFrame() { + if (frameRequested) { + frameRequested = false + clock.sendFrame(System.nanoTime()) + } + } + + override fun animate(): Boolean { + sendFrame() + return false + } + + override fun paint(g: com.codename1.ui.Graphics) { + super.paint(g) + } + + override fun initComponent() { + super.initComponent() + if (frameRequested) { + scheduleFrame() + } + } + + override fun deinitialize() { + animatedOn?.deregisterAnimated(this) + animatedOn = null + super.deinitialize() + } + + // --- measuring ---------------------------------------------------------- + + private var remeasurePending = false + + fun requestRemeasure() { + if (!remeasurePending) { + remeasurePending = true + setShouldCalcPreferredSize(true) + revalidateLater() + repaint() + } + } + + + /// The status bar and home indicator this host lies under, in pixels, as + /// of its last pass; read by its nodes' [SafeInsetsElement]s. + internal var insetTop = 0 + internal var insetBottom = 0 + private var insetsKnown = false + + internal fun layoutChildren() { + remeasurePending = false + val insets = SafeInsets.of(absoluteY, height) + val top = (insets ushr 32).toInt() + val bottom = insets.toInt() + val changed = !insetsKnown || top != insetTop || bottom != insetBottom + insetTop = top + insetBottom = bottom + insetsKnown = true + if (changed) { + // A node's measure is cached by constraints, and a safe area that + // arrives late (or a rotation) changes no constraint: forget the + // measures of the nodes that pad by it. Forgetting every node's + // rebuilt the lazy lists' items and cost a megabyte at start-up. + // This pass measures them; the nodes must not ask for another. + remeasurePending = true + forgetMeasures(this) + remeasurePending = false + } + val c = Constraints(0, width, 0, height) + for (k in 0 until componentCount) { + val child = getComponentAt(k) + val s = measureChild(child, c) + child.setX(if (child is UiNode) child.offsetX else 0) + child.setY(if (child is UiNode) child.offsetY else 0) + child.setWidth(s.w()) + child.setHeight(s.h()) + if (child is UiNode) { + child.applyPlacements() + } + } + } + + private fun forgetMeasures(c: Container) { + for (k in 0 until c.componentCount) { + val child = c.getComponentAt(k) + if (child is UiNode && child.usesSafeInsets) { + child.invalidateMeasure() + } + if (child is Container) { + forgetMeasures(child) + } + } + } + + internal fun preferred(): Dimension { + var w = 0 + var h = 0 + val insets = SafeInsets.of(absoluteY, cn1DisplayHeight()) + insetTop = (insets ushr 32).toInt() + insetBottom = insets.toInt() + // Measured with a guessed height: the next pass measures again. + insetsKnown = false + val c = Constraints(0, cn1DisplayWidth(), 0, Constraints.INF) + for (k in 0 until componentCount) { + val s = measureChild(getComponentAt(k), c) + w = Math.max(w, s.w()) + h = Math.max(h, s.h()) + } + return Dimension(w, h) + } +} + +internal object HostLayout : Layout() { + override fun layoutContainer(parent: Container) { + // Checked, never cast: a failed cast does not throw on iOS. + (parent as? ComposeHost)?.layoutChildren() + } + + override fun getPreferredSize(parent: Container): Dimension = + (parent as? ComposeHost)?.preferred() ?: Dimension(0, 0) +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Constraints.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Constraints.kt new file mode 100644 index 00000000000..5921f0239a0 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Constraints.kt @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import com.codename1.ui.CN + +/// The display's density in Compose terms: pixels per dp (1/160 inch). Read from +/// the port once a display exists; one pixel per dp before that, which only a +/// test that never starts a display sees. +internal object Density : androidx.compose.ui.unit.Density { + private var cached = 0f + + override val density: Float + get() { + if (cached == 0f) { + val perHundredMm = try { + CN.convertToPixels(100f) + } catch (t: Throwable) { + 0 + } + // 1 dp = 1/160 in = 0.15875 mm. + cached = if (perHundredMm > 0) perHundredMm / 100f * 0.15875f else 1f + } + return cached + } + + override val fontScale: Float get() = 1f + + val current: androidx.compose.ui.unit.Density get() = this + + fun px(dp: Dp): Int = if (dp.value.isNaN()) 0 else Math.round(dp.value * density) + + fun pxF(dp: Dp): Float = if (dp.value.isNaN()) 0f else dp.value * density + + /// For tests that measure in a known unit. + fun cn1Override(pixelsPerDp: Float) { + cached = pixelsPerDp + } +} + +internal val LTR: LayoutDirection = LayoutDirection.Ltr + +/// What a parent allows a child to be, in pixels. [INF] marks an unbounded axis. +internal class Constraints(val minWidth: Int, val maxWidth: Int, val minHeight: Int, val maxHeight: Int) { + val hasBoundedWidth: Boolean get() = maxWidth != INF + val hasBoundedHeight: Boolean get() = maxHeight != INF + val hasFixedWidth: Boolean get() = minWidth == maxWidth + val hasFixedHeight: Boolean get() = minHeight == maxHeight + + fun constrainWidth(w: Int): Int = Math.max(minWidth, Math.min(maxWidth, w)) + fun constrainHeight(h: Int): Int = Math.max(minHeight, Math.min(maxHeight, h)) + + fun copy(minWidth: Int = this.minWidth, maxWidth: Int = this.maxWidth, + minHeight: Int = this.minHeight, maxHeight: Int = this.maxHeight): Constraints = + Constraints(minWidth, Math.max(minWidth, maxWidth), minHeight, Math.max(minHeight, maxHeight)) + + /// Shrinks every bound by `horizontal` and `vertical` pixels (padding). + fun offset(horizontal: Int, vertical: Int): Constraints { + fun sub(v: Int, d: Int): Int = if (v == INF) INF else Math.max(0, v - d) + return Constraints(Math.max(0, minWidth - horizontal), sub(maxWidth, horizontal), + Math.max(0, minHeight - vertical), sub(maxHeight, vertical)) + } + + fun loose(): Constraints = Constraints(0, maxWidth, 0, maxHeight) + + override fun toString(): String = "Constraints(w=$minWidth..${s(maxWidth)}, h=$minHeight..${s(maxHeight)})" + + private fun s(v: Int): String = if (v == INF) "inf" else v.toString() + + companion object { + const val INF: Int = Int.MAX_VALUE + + fun fixed(w: Int, h: Int): Constraints = Constraints(w, w, h, h) + } +} + +/// The parts of the display the system draws over (the status bar and the home +/// indicator on a notched phone) that a composition's host actually lies under, +/// in pixels. M3's top app bar and navigation bar pad themselves by these, as +/// Compose's window insets make them do; each host keeps its own. Measured against the host, not the +/// display: a host below a form's title area, in a dialog or mid-form is not +/// under the status bar, and padding it by the display's inset would leave a +/// band of empty colour. +internal object SafeInsets { + /// The top and bottom insets of a host at `absY` that is `height` tall, + /// packed as `top shl 32 or bottom`. + fun of(absY: Int, height: Int): Long { + val r = try { + com.codename1.ui.Display.getInstance().getDisplaySafeArea(com.codename1.ui.geom.Rectangle()) + } catch (e: Throwable) { + null + } + if (r == null || height <= 0) { + return 0L + } + val t = Math.max(0, Math.min(height, r.y - absY)) + val b = Math.max(0, Math.min(height, absY + height - (r.y + r.height))) + return (t.toLong() shl 32) or b.toLong() + } +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Dispatch.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Dispatch.kt new file mode 100644 index 00000000000..b756463879e --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Dispatch.kt @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.runtime.snapshots.Snapshot +import com.codename1.ui.CN +import kotlinx.coroutines.CancellableContinuation +import kotlinx.coroutines.Delay +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.MainCoroutineDispatcher +import kotlinx.coroutines.internal.MainDispatcherFactory +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.coroutines.CoroutineContext + +/// `Dispatchers.Main`: Codename One's event dispatch thread. +@OptIn(InternalCoroutinesApi::class) +internal open class EdtDispatcher(private val eager: Boolean) : MainCoroutineDispatcher(), Delay { + override val immediate: MainCoroutineDispatcher get() = if (eager) this else Immediate + + override fun isDispatchNeeded(context: CoroutineContext): Boolean = !eager || !isEdt() + + override fun dispatch(context: CoroutineContext, block: Runnable) { + CN.callSerially(block) + } + + override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { + val t = CN.setTimeout(Math.max(0L, Math.min(timeMillis, Int.MAX_VALUE.toLong())).toInt()) { + continuation.resumeWith(Result.success(Unit)) + } + continuation.invokeOnCancellation { t.cancel() } + } + + override fun toString(): String = if (eager) "Dispatchers.Main.immediate" else "Dispatchers.Main" + + companion object { + val Main: EdtDispatcher = EdtDispatcher(false) + val Immediate: EdtDispatcher = EdtDispatcher(true) + + fun isEdt(): Boolean = try { + CN.isEdt() + } catch (t: Throwable) { + false + } + } +} + +/// Found by kotlinx.coroutines through `META-INF/services`, which the translator +/// compiles into a registry; on the JVM the real service loader reads the file. +@OptIn(InternalCoroutinesApi::class) +internal class EdtDispatcherFactory : MainDispatcherFactory { + override val loadPriority: Int get() = Int.MAX_VALUE + + override fun createDispatcher(allFactories: List): MainCoroutineDispatcher = EdtDispatcher.Main + + override fun hintOnError(): String = "Codename One's main dispatcher" +} + +/// Sends snapshot apply notifications on the EDT, so that a state written from +/// any thread schedules a recomposition. +internal object GlobalSnapshotManager { + private val started = AtomicBoolean(false) + private val pending = AtomicBoolean(false) + + fun ensureStarted() { + if (started.compareAndSet(false, true)) { + Snapshot.registerGlobalWriteObserver { + if (pending.compareAndSet(false, true)) { + CN.callSerially { + pending.set(false) + Snapshot.sendApplyNotifications() + } + } + } + } + } +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Elements.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Elements.kt new file mode 100644 index 00000000000..ff7c27b1656 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Elements.kt @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.unit.Dp + +// The modifier elements the public extension functions create. Each is a value +// (data class) where it can be, so that an unchanged chain compares equal and a +// recomposition that rebuilds it does not relayout. + +/// Changes how a node measures; applied in chain order. +internal sealed interface LayoutElement : Modifier.Element + +internal data class PaddingElement(val start: Dp, val top: Dp, val end: Dp, val bottom: Dp) : LayoutElement + +/// Padding by the system bars, read when the node is measured rather than when +/// it is composed: at the first composition the native view may not know its +/// safe area yet, and a value captured then would stay. The host re-measures +/// when the safe area changes (`ComposeHost.layoutChildren`). +internal data class SafeInsetsElement(val top: Boolean, val bottom: Boolean) : LayoutElement + +/// A size request. An unspecified bound leaves the incoming one alone; with +/// `enforceIncoming` the result still respects what the parent allows. +internal data class SizeElement( + val minWidth: Dp = Dp.Unspecified, val maxWidth: Dp = Dp.Unspecified, + val minHeight: Dp = Dp.Unspecified, val maxHeight: Dp = Dp.Unspecified, + val enforceIncoming: Boolean = true +) : LayoutElement + +internal data class FillElement(val horizontal: Boolean, val vertical: Boolean, val fraction: Float) : LayoutElement + +internal data class WrapElement(val horizontal: Boolean, val vertical: Boolean, val align: Alignment, + val unbounded: Boolean) : LayoutElement + +internal data class OffsetElement(val x: Dp, val y: Dp) : LayoutElement + +internal data class AspectRatioElement(val ratio: Float) : LayoutElement + +internal class ScrollElement(val state: ScrollStateHandle, val vertical: Boolean) : LayoutElement { + override fun equals(other: Any?): Boolean = + other is ScrollElement && other.state === state && other.vertical == vertical + + override fun hashCode(): Int = System.identityHashCode(state) * 2 + if (vertical) 1 else 0 +} + +/// Read by the parent's measure policy, not by the node. +internal sealed interface ParentDataElement : Modifier.Element + +internal data class WeightElement(val weight: Float, val fill: Boolean) : ParentDataElement + +internal data class AlignSelfElement(val horizontal: Alignment.Horizontal?, val vertical: Alignment.Vertical?, + val box: Alignment?) : ParentDataElement + +internal object MatchParentSizeElement : ParentDataElement + +/// Paints at the position it has in the chain. +internal sealed interface DrawElement : Modifier.Element + +internal data class BackgroundElement(val color: Color, val brush: Brush?, val shape: Shape) : DrawElement + +internal data class BorderElement(val width: Dp, val color: Color, val shape: Shape) : DrawElement + +internal data class ClipElement(val shape: Shape) : DrawElement + +internal class DrawBehindElement(val onDraw: DrawScope.() -> Unit) : DrawElement + +internal data class AlphaElement(val alpha: Float) : Modifier.Element + +internal class ClickableElement(val enabled: Boolean, val onClick: () -> Unit, val onLongClick: (() -> Unit)?, + val indication: Boolean) : Modifier.Element + +internal class PointerTapElement(val onTap: (Float, Float) -> Unit) : Modifier.Element + +internal data class TestTagElement(val tag: String) : Modifier.Element + +internal data class SemanticsElement(val contentDescription: String?) : Modifier.Element + +/// What a scroll modifier binds to; implemented by +/// [androidx.compose.foundation.ScrollState]. +internal interface ScrollStateHandle { + fun attach(node: UiNode, vertical: Boolean) + fun detach(node: UiNode) +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Emit.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Emit.kt new file mode 100644 index 00000000000..2beca85a9f3 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Emit.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ComposeNode +import androidx.compose.ui.Modifier + +/// Emits a layout node with children. +@Composable +internal inline fun LayoutNode(modifier: Modifier, policy: MeasurePolicy, content: @Composable () -> Unit) { + ComposeNode( + factory = { UiNode() }, + update = { + set(modifier) { this.modifier = it } + set(policy) { this.policy = it } + }, + content = content + ) +} + +/// Emits a node with no children. +@Composable +internal fun LeafNode(modifier: Modifier, policy: MeasurePolicy) { + ComposeNode( + factory = { UiNode() }, + update = { + set(modifier) { this.modifier = it } + set(policy) { this.policy = it } + } + ) +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Glyphs.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Glyphs.kt new file mode 100644 index 00000000000..b94ea9e7d13 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Glyphs.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.ui.graphics.vector.ImageVector + +internal object Glyphs { + fun of(v: ImageVector): Char = v.glyph +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Leaves.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Leaves.kt new file mode 100644 index 00000000000..8b9b4e3aaee --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Leaves.kt @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import com.codename1.ui.CN +import com.codename1.ui.Font +import com.codename1.ui.Graphics + +internal object Theme { + fun isDark(): Boolean = try { + CN.isDarkMode() == true + } catch (t: Throwable) { + false + } +} + +/// Native fonts by weight and pixel size, cached. +internal object Fonts { + private val cache = HashMap() + + fun get(sizePx: Int, weight: FontWeight?): Font { + val w = weight?.weight ?: 400 + val key = (sizePx.toLong() shl 16) or w.toLong() + cache[key]?.let { return it } + val f = create(sizePx, w) + cache[key] = f + return f + } + + private fun create(sizePx: Int, w: Int): Font { + try { + if (Font.isNativeFontSchemeSupported()) { + val name = when { + w <= 200 -> "native:MainThin" + w <= 300 -> "native:MainLight" + w <= 500 -> "native:MainRegular" + w <= 700 -> "native:MainBold" + else -> "native:MainBlack" + } + val base = Font.createTrueTypeFont(name, name) + if (base != null) { + return base.derive(sizePx.toFloat(), if (w >= 600) Font.STYLE_BOLD else Font.STYLE_PLAIN) + } + } + } catch (t: Throwable) { + // Fall through to a system font. + } + val size = when { + sizePx < Density.px(androidx.compose.ui.unit.Dp(13f)) -> Font.SIZE_SMALL + sizePx > Density.px(androidx.compose.ui.unit.Dp(18f)) -> Font.SIZE_LARGE + else -> Font.SIZE_MEDIUM + } + return Font.createSystemFont(Font.FACE_SYSTEM, if (w >= 600) Font.STYLE_BOLD else Font.STYLE_PLAIN, size) + } +} + +/// Text: wrapped at word boundaries to the width it is given, cut at +/// `maxLines` with an ellipsis when asked. +internal class TextNode : UiNode() { + var text: String = "" + set(v) { if (field != v) { field = v; invalidateMeasure(); repaint() } } + var style: TextStyle = TextStyle.Default + set(v) { if (field != v) { field = v; invalidateMeasure(); repaint() } } + var maxLines: Int = Int.MAX_VALUE + set(v) { if (field != v) { field = v; invalidateMeasure() } } + var softWrap: Boolean = true + set(v) { if (field != v) { field = v; invalidateMeasure() } } + var overflow: TextOverflow = TextOverflow.Clip + set(v) { if (field != v) { field = v; invalidateMeasure() } } + + private var lines: List = emptyList() + private var font: Font? = null + + init { + policy = LeafPolicy + } + + private fun font(): Font { + val sp = if (style.fontSize.isSpecified) style.fontSize.value else 14f + val f = Fonts.get(Math.max(1, Math.round(sp * Density.density)), style.fontWeight) + font = f + return f + } + + private fun lineHeight(f: Font): Int = + if (style.lineHeight.isSpecified && style.lineHeight.isSp) Math.max(f.height, Math.round(style.lineHeight.value * Density.density)) + else f.height + + override fun measureContent(c: Constraints): Long { + val f = font() + val maxW = c.maxWidth + val out = ArrayList() + for (para in text.split('\n')) { + if (!softWrap || maxW == Constraints.INF || f.stringWidth(para) <= maxW) { + out.add(para) + } else { + wrap(para, f, maxW, out) + } + if (out.size > maxLines) break + } + if (out.size > maxLines) { + while (out.size > maxLines) out.removeAt(out.size - 1) + if (overflow == TextOverflow.Ellipsis && out.isNotEmpty()) { + out[out.size - 1] = ellipsize(out[out.size - 1] + "\u2026", f, maxW) + } + } else if (overflow == TextOverflow.Ellipsis && maxW != Constraints.INF && out.size == 1 && f.stringWidth(out[0]) > maxW) { + out[0] = ellipsize(out[0], f, maxW) + } + lines = out + var w = 0 + for (l in out) w = Math.max(w, f.stringWidth(l)) + return size(Math.min(w, maxW), lineHeight(f) * out.size) + } + + private fun wrap(para: String, f: Font, maxW: Int, out: MutableList) { + var line = StringBuilder() + for (word in para.split(' ')) { + val candidate = if (line.isEmpty()) word else "$line $word" + if (f.stringWidth(candidate) <= maxW || line.isEmpty()) { + line = StringBuilder(candidate) + } else { + out.add(line.toString()) + line = StringBuilder(word) + } + } + out.add(line.toString()) + } + + private fun ellipsize(s: String, f: Font, maxW: Int): String { + val e = "\u2026" + var base = if (s.endsWith(e)) s.substring(0, s.length - 1) else s + if (maxW == Constraints.INF || f.stringWidth(base + e) <= maxW && s.endsWith(e)) return base + e + while (base.isNotEmpty() && f.stringWidth(base + e) > maxW) base = base.substring(0, base.length - 1) + return base.trimEnd() + e + } + + override fun paintContent(g: Graphics) { + val f = font ?: font() + val color = if (style.color == Color.Unspecified) Color.Black else style.color + if (color.alpha <= 0f) return + val old = g.alpha + g.font = f + g.color = color.argb and 0xffffff + if (color.alpha < 1f) g.concatenateAlpha(Math.round(color.alpha * 255)) + g.isAntiAliased = true + g.isAntiAliasedText = true + val lh = lineHeight(f) + var y = absY() + contentY + (lh - f.height) / 2 + val x0 = absX() + contentX + for (l in lines) { + val lw = f.stringWidth(l) + val dx = when (style.textAlign) { + TextAlign.Center -> (contentW - lw) / 2 + TextAlign.End, TextAlign.Right -> contentW - lw + else -> 0 + } + g.drawString(l, x0 + dx, y) + if (style.textDecoration == TextDecoration.Underline) { + g.drawLine(x0 + dx, y + f.ascent + 1, x0 + dx + lw, y + f.ascent + 1) + } else if (style.textDecoration == TextDecoration.LineThrough) { + g.drawLine(x0 + dx, y + f.height / 2, x0 + dx + lw, y + f.height / 2) + } + y += lh + } + g.alpha = old + } + + fun cn1Lines(): List = lines +} + +internal fun UiNode.absX(): Int = x +internal fun UiNode.absY(): Int = y + +/// A bitmap, scaled into its content box. +internal class ImageNode : UiNode() { + var bitmap: ImageBitmap? = null + set(v) { if (field !== v) { field = v; invalidateMeasure(); repaint() } } + var contentScale: ContentScale = ContentScale.Fit + var tint: Color = Color.Unspecified + + init { + policy = LeafPolicy + } + + override fun measureContent(c: Constraints): Long { + val b = bitmap ?: return size(c.minWidth, c.minHeight) + val iw = b.width + val ih = b.height + if (c.hasFixedWidth && c.hasFixedHeight) return size(c.maxWidth, c.maxHeight) + var w = c.constrainWidth(iw) + var h = c.constrainHeight(ih) + if (iw > 0 && ih > 0) { + val sw = w.toFloat() / iw + val sh = h.toFloat() / ih + val s = Math.min(sw, sh) + if (!c.hasFixedWidth) w = c.constrainWidth(Math.round(iw * s)) + if (!c.hasFixedHeight) h = c.constrainHeight(Math.round(ih * s)) + } + return size(w, h) + } + + override fun paintContent(g: Graphics) { + val b = bitmap ?: return + val bw = contentW + val bh = contentH + val iw = b.width.toFloat() + val ih = b.height.toFloat() + if (iw <= 0 || ih <= 0) return + val s = when (contentScale) { + ContentScale.Crop -> Math.max(bw / iw, bh / ih) + ContentScale.FillBounds -> -1f + ContentScale.None -> 1f + ContentScale.FillWidth -> bw / iw + else -> Math.min(bw / iw, bh / ih) + } + val x0 = x + contentX + val y0 = y + contentY + if (s < 0) { + g.drawImage(b.image, x0, y0, bw, bh) + return + } + val dw = Math.round(iw * s) + val dh = Math.round(ih * s) + g.pushClip() + g.clipRect(x0, y0, bw, bh) + g.drawImage(b.image, x0 + (bw - dw) / 2, y0 + (bh - dh) / 2, dw, dh) + g.popClip() + } +} + +/// A material icon glyph from the material design font. +internal class IconNode : UiNode() { + var glyph: Char = ' ' + set(v) { if (field != v) { field = v; repaint() } } + var tint: Color = Color.Black + set(v) { if (field != v) { field = v; repaint() } } + + init { + policy = LeafPolicy + } + + override fun measureContent(c: Constraints): Long { + val d = Density.px(androidx.compose.ui.unit.Dp(24f)) + return size(c.constrainWidth(d), c.constrainHeight(d)) + } + + override fun paintContent(g: Graphics) { + val s = Math.min(contentW, contentH) + if (s <= 0 || tint.alpha <= 0f) return + val f = com.codename1.ui.FontImage.getMaterialDesignFont().derive(s.toFloat(), Font.STYLE_PLAIN) + val old = g.alpha + g.font = f + g.color = tint.argb and 0xffffff + if (tint.alpha < 1f) g.concatenateAlpha(Math.round(tint.alpha * 255)) + g.isAntiAliasedText = true + val str = glyph.toString() + val gw = f.stringWidth(str) + g.drawString(str, x + contentX + (contentW - gw) / 2, y + contentY + (contentH - f.height) / 2) + g.alpha = old + } +} + +/// Hosts a Codename One component as a node's only child, sized to the node's +/// content box: how text input gets the platform's own editing. +internal class NativeNode(val component: T) : UiNode() { + init { + policy = BoxPolicy(androidx.compose.ui.Alignment.TopStart, true) + addComponent(component) + } +} + +/// A single line or multi line text input over Codename One's `TextArea`. +internal class TextInputNode : UiNode() { + val field: com.codename1.ui.TextArea + private var syncing = false + var onValueChange: (String) -> Unit = {} + var textColor: Color = Color.Black + set(v) { field = v; applyColors() } + var font: Font? = null + set(v) { field = v; if (v != null) this.field.allStyles.font = v; invalidateMeasure() } + + init { + policy = BoxPolicy(androidx.compose.ui.Alignment.TopStart, true) + val input = com.codename1.ui.TextField() + field = input + cn1StripStyle(input, allStates = true) + input.addDataChangedListener { _, _ -> + if (!syncing) onValueChange(input.text) + } + addComponent(field) + } + + fun setValue(v: String) { + if (field.text != v) { + syncing = true + try { + field.text = v + } finally { + syncing = false + } + } + } + + var singleLine: Boolean = true + set(v) { field = v; this.field.isSingleLineTextArea = v; invalidateMeasure() } + + var hint: String = "" + set(v) { field = v; this.field.hint = v } + + var enabledInput: Boolean = true + set(v) { field = v; this.field.isEnabled = v } + + private fun applyColors() { + field.allStyles.fgColor = textColor.argb and 0xffffff + field.hintLabel?.allStyles?.fgColor = textColor.argb and 0xffffff + field.hintLabel?.allStyles?.opacity = 140 + } +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Painting.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Painting.kt new file mode 100644 index 00000000000..85696f6e843 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Painting.kt @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.LinearGradient +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.DrawStyle +import androidx.compose.ui.graphics.drawscope.Stroke +import com.codename1.ui.Graphics +import com.codename1.ui.geom.GeneralPath + +/// Fills and strokes shapes on Codename One graphics. +internal object Painting { + fun outline(shape: Shape, w: Int, h: Int): Outline = + shape.createOutline(Size(w.toFloat(), h.toFloat()), LTR, Density) + + fun fill(g: Graphics, shape: Shape, color: Color, brush: Brush?, x: Int, y: Int, w: Int, h: Int) { + if (w <= 0 || h <= 0) return + val o = outline(shape, w, h) + val b = brush + if (b is LinearGradient && b.colors.size >= 2 && o is Outline.Rectangle) { + val a = b.colors[0] + val e = b.colors[b.colors.size - 1] + val old = g.alpha + g.concatenateAlpha(Math.round(a.alpha * 255)) + g.fillLinearGradient(a.argb and 0xffffff, e.argb and 0xffffff, x, y, w, h, !b.vertical) + g.alpha = old + return + } + val c = when (b) { + is SolidColor -> b.value + is LinearGradient -> if (b.colors.isEmpty()) color else b.colors[0] + else -> color + } + if (c.alpha <= 0f) return + val old = g.alpha + g.color = c.argb and 0xffffff + if (c.alpha < 1f) g.concatenateAlpha(Math.round(c.alpha * 255)) + when (o) { + is Outline.Rounded -> { + if (uniform(o)) { + val r = Math.round(o.topLeft * 2) + g.fillRoundRect(x, y, w, h, r, r) + } else if (g.isShapeSupported) { + g.fillShape(roundedPath(o, x.toFloat(), y.toFloat())) + } else { + val r = o.cornerArc() + g.fillRoundRect(x, y, w, h, r, r) + } + } + else -> g.fillRect(x, y, w, h) + } + g.alpha = old + } + + fun stroke(g: Graphics, shape: Shape, color: Color, width: Int, x: Int, y: Int, w: Int, h: Int) { + if (w <= 0 || h <= 0 || width <= 0 || color.alpha <= 0f) return + val old = g.alpha + g.color = color.argb and 0xffffff + if (color.alpha < 1f) g.concatenateAlpha(Math.round(color.alpha * 255)) + val o = outline(shape, w, h) + val half = width / 2f + if (g.isShapeSupported) { + val path = when (o) { + is Outline.Rounded -> roundedPath(Outline.Rounded(w - width.toFloat(), h - width.toFloat(), + Math.max(0f, o.topLeft - half), Math.max(0f, o.topRight - half), + Math.max(0f, o.bottomRight - half), Math.max(0f, o.bottomLeft - half)), x + half, y + half) + else -> rectPath(x + half, y + half, w - width.toFloat(), h - width.toFloat()) + } + g.drawShape(path, com.codename1.ui.Stroke(width.toFloat(), com.codename1.ui.Stroke.CAP_BUTT, + com.codename1.ui.Stroke.JOIN_MITER, 4f)) + } else { + for (i in 0 until width) { + when (o) { + is Outline.Rounded -> { + val r = o.cornerArc() + g.drawRoundRect(x + i, y + i, w - 1 - 2 * i, h - 1 - 2 * i, r, r) + } + else -> g.drawRect(x + i, y + i, w - 1 - 2 * i, h - 1 - 2 * i) + } + } + } + g.alpha = old + } + + private fun uniform(o: Outline.Rounded): Boolean = + o.topLeft == o.topRight && o.topRight == o.bottomRight && o.bottomRight == o.bottomLeft + + fun rectPath(x: Float, y: Float, w: Float, h: Float): GeneralPath { + val p = GeneralPath() + p.moveTo(x, y) + p.lineTo(x + w, y) + p.lineTo(x + w, y + h) + p.lineTo(x, y + h) + p.closePath() + return p + } + + fun roundedPath(o: Outline.Rounded, x: Float, y: Float): GeneralPath { + val w = o.width + val h = o.height + val max = Math.min(w, h) / 2f + val tl = Math.min(o.topLeft, max) + val tr = Math.min(o.topRight, max) + val br = Math.min(o.bottomRight, max) + val bl = Math.min(o.bottomLeft, max) + val p = GeneralPath() + p.moveTo(x + tl, y) + p.lineTo(x + w - tr, y) + if (tr > 0) p.arc(x + w - 2 * tr, y, 2 * tr, 2 * tr, HALF_PI, -HALF_PI, true) + p.lineTo(x + w, y + h - br) + if (br > 0) p.arc(x + w - 2 * br, y + h - 2 * br, 2 * br, 2 * br, 0f, -HALF_PI, true) + p.lineTo(x + bl, y + h) + if (bl > 0) p.arc(x, y + h - 2 * bl, 2 * bl, 2 * bl, -HALF_PI, -HALF_PI, true) + p.lineTo(x, y + tl) + if (tl > 0) p.arc(x, y, 2 * tl, 2 * tl, PI, -HALF_PI, true) + p.closePath() + return p + } + + private const val PI = Math.PI.toFloat() + private const val HALF_PI = (Math.PI / 2).toFloat() +} + +/// [DrawScope] over Codename One graphics, already translated to the box's +/// origin. +internal class Cn1DrawScope(private val g: Graphics, override val size: Size) : DrawScope { + override val density: Float get() = Density.density + override val fontScale: Float get() = 1f + + private inline fun paint(color: Color, alpha: Float, block: () -> Unit) { + val a = color.alpha * alpha + if (a <= 0f) return + val old = g.alpha + g.color = color.argb and 0xffffff + if (a < 1f) g.concatenateAlpha(Math.round(a * 255)) + g.isAntiAliased = true + try { + block() + } finally { + g.alpha = old + } + } + + private fun stroke(width: Float, cap: StrokeCap): com.codename1.ui.Stroke = + com.codename1.ui.Stroke(Math.max(1f, width), when (cap) { + StrokeCap.Round -> com.codename1.ui.Stroke.CAP_ROUND + StrokeCap.Square -> com.codename1.ui.Stroke.CAP_SQUARE + else -> com.codename1.ui.Stroke.CAP_BUTT + }, com.codename1.ui.Stroke.JOIN_ROUND, 4f) + + private fun r(v: Float): Int = Math.round(v) + + override fun drawRect(color: Color, topLeft: Offset, size: Size, alpha: Float, style: DrawStyle) = paint(color, alpha) { + if (style is Stroke) { + g.drawShape(Painting.rectPath(topLeft.x, topLeft.y, size.width, size.height), stroke(style.width, style.cap)) + } else { + g.fillRect(r(topLeft.x), r(topLeft.y), r(size.width), r(size.height)) + } + } + + override fun drawRoundRect(color: Color, topLeft: Offset, size: Size, cornerRadius: CornerRadius, alpha: Float, + style: DrawStyle) = paint(color, alpha) { + val rr = cornerRadius.x + if (style is Stroke) { + g.drawShape(Painting.roundedPath(Outline.Rounded(size.width, size.height, rr, rr, rr, rr), topLeft.x, topLeft.y), + stroke(style.width, style.cap)) + } else { + g.fillRoundRect(r(topLeft.x), r(topLeft.y), r(size.width), r(size.height), r(rr * 2), r(cornerRadius.y * 2)) + } + } + + override fun drawCircle(color: Color, radius: Float, center: Offset, alpha: Float, style: DrawStyle) = paint(color, alpha) { + if (style is Stroke) { + val p = GeneralPath() + p.arc(center.x - radius, center.y - radius, radius * 2, radius * 2, 0f, (Math.PI * 2).toFloat()) + g.drawShape(p, stroke(style.width, style.cap)) + } else { + g.fillArc(r(center.x - radius), r(center.y - radius), r(radius * 2), r(radius * 2), 0, 360) + } + } + + override fun drawArc(color: Color, startAngle: Float, sweepAngle: Float, useCenter: Boolean, topLeft: Offset, + size: Size, alpha: Float, style: DrawStyle) = paint(color, alpha) { + // Compose measures angles clockwise; Codename One counter-clockwise. + if (style is Stroke) { + val p = GeneralPath() + val rad = (Math.PI / 180).toFloat() + p.arc(topLeft.x, topLeft.y, size.width, size.height, -startAngle * rad, -sweepAngle * rad) + g.drawShape(p, stroke(style.width, style.cap)) + } else { + g.fillArc(r(topLeft.x), r(topLeft.y), r(size.width), r(size.height), r(-startAngle), r(-sweepAngle)) + } + } + + override fun drawLine(color: Color, start: Offset, end: Offset, strokeWidth: Float, cap: StrokeCap, alpha: Float) = + paint(color, alpha) { + if (strokeWidth <= 1f) { + g.drawLine(r(start.x), r(start.y), r(end.x), r(end.y)) + } else { + val p = GeneralPath() + p.moveTo(start.x, start.y) + p.lineTo(end.x, end.y) + g.drawShape(p, stroke(strokeWidth, cap)) + } + } + + override fun drawImage(image: ImageBitmap, topLeft: Offset, alpha: Float) = paint(Color.Black, alpha) { + g.drawImage(image.image, r(topLeft.x), r(topLeft.y)) + } + + override fun drawPolyline(color: Color, points: List, strokeWidth: Float, alpha: Float) = paint(color, alpha) { + if (points.size >= 2) { + val p = GeneralPath() + p.moveTo(points[0].x, points[0].y) + for (i in 1 until points.size) p.lineTo(points[i].x, points[i].y) + g.drawShape(p, stroke(strokeWidth, StrokeCap.Round)) + } + } + + override fun drawPolygon(color: Color, points: List, alpha: Float) = paint(color, alpha) { + if (points.size >= 3) { + val p = GeneralPath() + p.moveTo(points[0].x, points[0].y) + for (i in 1 until points.size) p.lineTo(points[i].x, points[i].y) + p.closePath() + g.fillShape(p) + } + } + + override fun translate(left: Float, top: Float, block: DrawScope.() -> Unit) { + val dx = r(left) + val dy = r(top) + g.translate(dx, dy) + try { + block() + } finally { + g.translate(-dx, -dy) + } + } +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Policies.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Policies.kt new file mode 100644 index 00000000000..de455f8258a --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Policies.kt @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.ui.Alignment +import androidx.compose.ui.unit.IntSize +import com.codename1.ui.Component + +private fun Component.weightOf(): WeightElement? = (this as? UiNode)?.weight +private fun Component.alignOf(): AlignSelfElement? = (this as? UiNode)?.alignSelf + +/// Stacks children, each aligned in the box. +internal class BoxPolicy(val alignment: Alignment, val propagateMinConstraints: Boolean) : MeasurePolicy { + override fun measure(node: UiNode, children: List, constraints: Constraints): Long { + val childC = if (propagateMinConstraints) constraints else constraints.loose() + var w = constraints.minWidth + var h = constraints.minHeight + val sizes = LongArray(children.size) + // Children that match the parent's size are measured once the others have + // decided it. + for (i in children.indices) { + val c = children[i] + if ((c as? UiNode)?.matchParentSize == true) continue + val s = measureChild(c, childC) + sizes[i] = s + w = Math.max(w, s.w()) + h = Math.max(h, s.h()) + } + w = constraints.constrainWidth(w) + h = constraints.constrainHeight(h) + for (i in children.indices) { + val c = children[i] + if ((c as? UiNode)?.matchParentSize == true) { + sizes[i] = measureChild(c, Constraints.fixed(w, h)) + } + val s = sizes[i] + val a = c.alignOf()?.box ?: alignment + val o = a.align(IntSize(s.w(), s.h()), IntSize(w, h), LTR) + node.place(i, o.x, o.y, s.w(), s.h()) + } + return size(w, h) + } + + override fun equals(other: Any?): Boolean = + other is BoxPolicy && other.alignment == alignment && other.propagateMinConstraints == propagateMinConstraints + + override fun hashCode(): Int = alignment.hashCode() * 2 + if (propagateMinConstraints) 1 else 0 + + companion object { + val TopStart: BoxPolicy = BoxPolicy(Alignment.TopStart, false) + } +} + +/// Row and Column: children along the main axis, with weights and an +/// arrangement, aligned on the cross axis. +internal class LinearPolicy( + val vertical: Boolean, + val horizontalArrangement: Arrangement.Horizontal?, + val verticalArrangement: Arrangement.Vertical?, + val horizontalAlignment: Alignment.Horizontal?, + val verticalAlignment: Alignment.Vertical? +) : MeasurePolicy { + override fun measure(node: UiNode, children: List, constraints: Constraints): Long { + val d = Density + val spacing = if (vertical) d.px(verticalArrangement!!.spacing) else d.px(horizontalArrangement!!.spacing) + val mainMax = if (vertical) constraints.maxHeight else constraints.maxWidth + val mainMin = if (vertical) constraints.minHeight else constraints.minWidth + val crossMax = if (vertical) constraints.maxWidth else constraints.maxHeight + val crossMin = if (vertical) constraints.minWidth else constraints.minHeight + val n = children.size + val mains = IntArray(n) + val crosses = IntArray(n) + var used = if (n > 1) spacing * (n - 1) else 0 + var totalWeight = 0f + var cross = 0 + for (i in 0 until n) { + val c = children[i] + val wt = c.weightOf() + if (wt != null && wt.weight > 0f) { + totalWeight += wt.weight + continue + } + val remaining = if (mainMax == Constraints.INF) Constraints.INF else Math.max(0, mainMax - used) + val cc = if (vertical) Constraints(0, crossMax, 0, remaining) else Constraints(0, remaining, 0, crossMax) + val s = measureChild(c, cc) + mains[i] = if (vertical) s.h() else s.w() + crosses[i] = if (vertical) s.w() else s.h() + used += mains[i] + cross = Math.max(cross, crosses[i]) + } + if (totalWeight > 0f) { + val space = if (mainMax == Constraints.INF) 0 else Math.max(0, mainMax - used) + var left = space + var lastWeighted = -1 + for (i in 0 until n) { + val wt = children[i].weightOf() + if (wt != null && wt.weight > 0f) lastWeighted = i + } + for (i in 0 until n) { + val c = children[i] + val wt = c.weightOf() ?: continue + if (wt.weight <= 0f) continue + val share = if (i == lastWeighted) left else Math.round(space * wt.weight / totalWeight) + left -= share + val min = if (wt.fill) share else 0 + val cc = if (vertical) Constraints(0, crossMax, min, share) else Constraints(min, share, 0, crossMax) + val s = measureChild(c, cc) + mains[i] = if (vertical) s.h() else s.w() + crosses[i] = if (vertical) s.w() else s.h() + used += mains[i] + cross = Math.max(cross, crosses[i]) + } + } + val mainSize = Math.max(mainMin, if (totalWeight > 0f && mainMax != Constraints.INF) mainMax else + Math.min(used, mainMax)) + val crossSize = Math.max(crossMin, Math.min(cross, crossMax)) + // Arrange along the main axis. + val positions = IntArray(n) + if (vertical) { + verticalArrangement!!.arrange(mainSize, mains, positions) + } else { + horizontalArrangement!!.arrange(mainSize, mains, LTR, positions) + } + for (i in 0 until n) { + val c = children[i] + val a = c.alignOf() + val crossPos = if (vertical) { + (a?.horizontal ?: horizontalAlignment!!).align(crosses[i], crossSize, LTR) + } else { + (a?.vertical ?: verticalAlignment!!).align(crosses[i], crossSize) + } + if (vertical) { + node.place(i, crossPos, positions[i], crosses[i], mains[i]) + } else { + node.place(i, positions[i], crossPos, mains[i], crosses[i]) + } + } + return if (vertical) size(crossSize, mainSize) else size(mainSize, crossSize) + } + + override fun equals(other: Any?): Boolean = other is LinearPolicy && other.vertical == vertical && + other.horizontalArrangement == horizontalArrangement && other.verticalArrangement == verticalArrangement && + other.horizontalAlignment == horizontalAlignment && other.verticalAlignment == verticalAlignment + + override fun hashCode(): Int = (if (vertical) 1 else 0) + 31 * (horizontalArrangement.hashCode() + + 31 * (verticalArrangement.hashCode() + 31 * (horizontalAlignment.hashCode() + 31 * verticalAlignment.hashCode()))) +} + +/// A node with no children whose content measures itself (text, an icon, a +/// canvas). +internal object LeafPolicy : MeasurePolicy { + override fun measure(node: UiNode, children: List, constraints: Constraints): Long = + size(constraints.minWidth, constraints.minHeight) +} diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt new file mode 100644 index 00000000000..22a95da6fb1 --- /dev/null +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose.internal + +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Shape +import com.codename1.ui.Component +import com.codename1.ui.Container +import com.codename1.ui.Graphics +import com.codename1.ui.geom.Dimension +import com.codename1.ui.layouts.Layout +import com.codename1.ui.plaf.Border + +/// Lays out the children of a node. `measure` returns the content size and +/// records, through [UiNode.place], where each child goes relative to the content +/// box's top left corner. +internal interface MeasurePolicy { + fun measure(node: UiNode, children: List, constraints: Constraints): Long +} + +internal fun size(w: Int, h: Int): Long = (w.toLong() shl 32) or (h.toLong() and 0xffffffffL) +internal fun Long.w(): Int = (this ushr 32).toInt() +internal fun Long.h(): Int = this.toInt() + +/// A Compose layout node, which is also a Codename One container: the node's +/// children are Codename One components, and so is everything a composable emits. +/// +/// Compose's layout model is reproduced here rather than mapped onto Codename One +/// layouts. A node is measured with [Constraints] and answers a size; its modifier +/// chain is walked outermost first, each layout element transforming the +/// constraints going in and the size coming out (padding shrinks one and grows the +/// other); the innermost box is where [policy] measures the children. Drawing +/// elements paint at the box their position in the chain gives them. +/// +/// Measuring happens from the root of a composition ([ComposeHost]) in one pass, +/// which also fixes every child's position. Codename One's own layout pass then +/// only copies those positions out ([NodeLayout]); a node it lays out on its own +/// re-measures at its current size. +internal open class UiNode : Container(NodeLayout) { + var policy: MeasurePolicy = BoxPolicy.TopStart + set(value) { + if (field != value) { + field = value + invalidateMeasure() + } + } + + var modifier: Modifier = Modifier + set(value) { + if (field != value) { + val old = field + field = value + resolveModifier(old) + invalidateMeasure() + repaint() + } + } + + // The resolved chain. + internal var chain: Array = EMPTY + internal var weight: WeightElement? = null + internal var alignSelf: AlignSelfElement? = null + internal var matchParentSize: Boolean = false + internal var alpha: Float = 1f + internal var clickable: ClickableElement? = null + internal var tap: PointerTapElement? = null + private var scroll: ScrollElement? = null + + // Measure results: the size at every chain position, and the box each one + // occupies once placed (x, y, w, h per element, then the content box). + private var sizes = LongArray(1) + private var boxes = IntArray(4) + internal var measured = false + + /// Whether a [SafeInsetsElement] is in the chain, so the host re-measures + /// this node (and only it, with its ancestors) when the safe area changes. + internal var usesSafeInsets = false + + /// The top inset this node was measured with, so placing it cannot read a + /// newer one. + private var placedInsetTop = 0 + private var lastConstraints: Constraints? = null + private var placements = IntArray(0) + internal var contentX = 0 + internal var contentY = 0 + internal var contentW = 0 + internal var contentH = 0 + + // Offset modifiers move the whole node when its parent places it. + internal var offsetX = 0 + internal var offsetY = 0 + + init { + cn1StripStyle(this) + } + + // --- modifier resolution ------------------------------------------------ + + private fun resolveModifier(old: Modifier) { + val list = ArrayList() + modifier.foldIn(list) { acc, e -> acc.add(e); acc } + var weight: WeightElement? = null + var align: AlignSelfElement? = null + var match = false + var alpha = 1f + var click: ClickableElement? = null + var tap: PointerTapElement? = null + var scroll: ScrollElement? = null + var tag: String? = null + var description: String? = null + val chain = ArrayList(list.size) + for (e in list) { + when (e) { + is WeightElement -> weight = e + is AlignSelfElement -> align = e + is MatchParentSizeElement -> match = true + is AlphaElement -> alpha *= e.alpha + is ClickableElement -> click = e + is PointerTapElement -> tap = e + is TestTagElement -> tag = e.tag + is SemanticsElement -> description = e.contentDescription + is ScrollElement -> { + scroll = e + chain.add(e) + } + is LayoutElement, is DrawElement -> chain.add(e) + else -> {} + } + } + this.chain = chain.toTypedArray() + var insets = false + for (k in 0 until chain.size) { + if (chain[k] is SafeInsetsElement) { + insets = true + } + } + usesSafeInsets = insets + this.weight = weight + this.alignSelf = align + this.matchParentSize = match + this.alpha = alpha + this.clickable = click + this.tap = tap + if (this.scroll?.state !== scroll?.state) { + this.scroll?.state?.detach(this) + scroll?.state?.attach(this, scroll.vertical) + } + this.scroll = scroll + isScrollableY = (scroll != null && scroll.vertical) || lazyAxis() == 1 + isScrollableX = (scroll != null && !scroll.vertical) || lazyAxis() == 2 + val interactive = (click != null && click.enabled) || tap != null + isGrabsPointerEvents = interactive + isFocusable = interactive + if (tag != null) { + name = tag + } + if (description != null) { + accessibilityText = description + } + sizes = LongArray(this.chain.size + 1) + boxes = IntArray((this.chain.size + 1) * 4) + } + + // --- measuring ---------------------------------------------------------- + + /// Marks this node and every ancestor up to the composition's root as needing + /// a new measure pass, and asks the host for one. + fun invalidateMeasure() { + var n: Container? = this + while (n != null) { + if (n is UiNode) { + if (!n.measured && n !== this) { + break + } + n.measured = false + n.setShouldCalcPreferredSize(true) + } else if (n is ComposeHost) { + n.requestRemeasure() + break + } + n = n.parent + } + } + + /// The host this node is composed into, or null while it is detached. + private fun composeHost(): ComposeHost? { + var n: Container? = parent + while (n != null) { + if (n is ComposeHost) { + return n + } + n = n.parent + } + return null + } + + /// Measures this node under `c` and answers its size (packed with [size]). + fun measure(c: Constraints): Long { + val last = lastConstraints + if (measured && last != null && last.minWidth == c.minWidth && last.maxWidth == c.maxWidth && + last.minHeight == c.minHeight && last.maxHeight == c.maxHeight) { + return sizes[0] + } + val result = measureChain(0, c) + lastConstraints = c + measured = true + placeChain() + return result + } + + val measuredWidth: Int get() = sizes[0].w() + val measuredHeight: Int get() = sizes[0].h() + + private fun measureChain(i: Int, c: Constraints): Long { + if (i == chain.size) { + val s = measureContent(c) + val r = size(c.constrainWidth(s.w()), c.constrainHeight(s.h())) + sizes[i] = r + return r + } + val d = Density + val r: Long = when (val e = chain[i]) { + is SafeInsetsElement -> { + val host = composeHost() + placedInsetTop = if (e.top && host != null) host.insetTop else 0 + val v = placedInsetTop + (if (e.bottom && host != null) host.insetBottom else 0) + val inner = measureChain(i + 1, c.offset(0, v)) + size(c.constrainWidth(inner.w()), c.constrainHeight(inner.h() + v)) + } + is PaddingElement -> { + val l = d.px(e.start) + val t = d.px(e.top) + val h = l + d.px(e.end) + val v = t + d.px(e.bottom) + val inner = measureChain(i + 1, c.offset(h, v)) + size(c.constrainWidth(inner.w() + h), c.constrainHeight(inner.h() + v)) + } + is SizeElement -> { + val inner = measureChain(i + 1, sizeConstraints(e, c)) + size(c.constrainWidth(inner.w()), c.constrainHeight(inner.h())) + } + is FillElement -> { + var minW = c.minWidth + var maxW = c.maxWidth + var minH = c.minHeight + var maxH = c.maxHeight + if (e.horizontal && c.hasBoundedWidth) { + val w = c.constrainWidth(Math.round(c.maxWidth * e.fraction)) + minW = w + maxW = w + } + if (e.vertical && c.hasBoundedHeight) { + val h = c.constrainHeight(Math.round(c.maxHeight * e.fraction)) + minH = h + maxH = h + } + val inner = measureChain(i + 1, Constraints(minW, maxW, minH, maxH)) + size(c.constrainWidth(inner.w()), c.constrainHeight(inner.h())) + } + is WrapElement -> { + val inner = measureChain(i + 1, Constraints( + if (e.horizontal) 0 else c.minWidth, + if (e.horizontal && e.unbounded) Constraints.INF else c.maxWidth, + if (e.vertical) 0 else c.minHeight, + if (e.vertical && e.unbounded) Constraints.INF else c.maxHeight)) + size(c.constrainWidth(inner.w()), c.constrainHeight(inner.h())) + } + is AspectRatioElement -> { + var cc = c + if (c.hasBoundedWidth && !c.hasFixedHeight) { + val h = c.constrainHeight(Math.round(c.maxWidth / e.ratio)) + cc = Constraints(c.maxWidth, c.maxWidth, h, h) + } else if (c.hasBoundedHeight) { + val w = c.constrainWidth(Math.round(c.maxHeight * e.ratio)) + cc = Constraints(w, w, c.maxHeight, c.maxHeight) + } + val inner = measureChain(i + 1, cc) + size(c.constrainWidth(inner.w()), c.constrainHeight(inner.h())) + } + is ScrollElement -> { + val inner = if (e.vertical) { + measureChain(i + 1, Constraints(c.minWidth, c.maxWidth, 0, Constraints.INF)) + } else { + measureChain(i + 1, Constraints(0, Constraints.INF, c.minHeight, c.maxHeight)) + } + size(c.constrainWidth(inner.w()), c.constrainHeight(inner.h())) + } + else -> measureChain(i + 1, c) + } + sizes[i] = r + return r + } + + private fun sizeConstraints(e: SizeElement, c: Constraints): Constraints { + val d = Density + var minW = if (e.minWidth.value.isNaN()) -1 else d.px(e.minWidth) + var maxW = if (e.maxWidth.value.isNaN()) -1 else if (e.maxWidth.value.isInfinite()) Constraints.INF else d.px(e.maxWidth) + var minH = if (e.minHeight.value.isNaN()) -1 else d.px(e.minHeight) + var maxH = if (e.maxHeight.value.isNaN()) -1 else if (e.maxHeight.value.isInfinite()) Constraints.INF else d.px(e.maxHeight) + if (e.enforceIncoming) { + if (minW >= 0) minW = c.constrainWidth(minW) + if (maxW >= 0) maxW = c.constrainWidth(maxW) + if (minH >= 0) minH = c.constrainHeight(minH) + if (maxH >= 0) maxH = c.constrainHeight(maxH) + } + val rMinW = if (minW >= 0) minW else if (maxW >= 0) Math.min(c.minWidth, maxW) else c.minWidth + val rMaxW = if (maxW >= 0) Math.max(maxW, rMinW) else Math.max(c.maxWidth, rMinW) + val rMinH = if (minH >= 0) minH else if (maxH >= 0) Math.min(c.minHeight, maxH) else c.minHeight + val rMaxH = if (maxH >= 0) Math.max(maxH, rMinH) else Math.max(c.maxHeight, rMinH) + return Constraints(rMinW, rMaxW, rMinH, rMaxH) + } + + /// Measures what is inside the innermost box. Containers run [policy] over + /// their children; leaves override this. + protected open fun measureContent(c: Constraints): Long { + val kids = ArrayList(componentCount) + for (k in 0 until componentCount) { + kids.add(getComponentAt(k)) + } + if (placements.size != kids.size * 4) { + placements = IntArray(kids.size * 4) + } + return policy.measure(this, kids, c) + } + + /// Records where a child goes, relative to the content box. + fun place(index: Int, x: Int, y: Int, w: Int, h: Int) { + if (placements.size < (index + 1) * 4) { + placements = placements.copyOf((index + 1) * 4) + } + placements[index * 4] = x + placements[index * 4 + 1] = y + placements[index * 4 + 2] = w + placements[index * 4 + 3] = h + } + + /// Fixes each chain position's box, outermost first. + private fun placeChain() { + val d = Density + var x = 0 + var y = 0 + offsetX = 0 + offsetY = 0 + var leading = true + for (i in 0..chain.size) { + val w = sizes[i].w() + val h = sizes[i].h() + boxes[i * 4] = x + boxes[i * 4 + 1] = y + boxes[i * 4 + 2] = w + boxes[i * 4 + 3] = h + if (i == chain.size) { + break + } + val e = chain[i] + val nw = sizes[i + 1].w() + val nh = sizes[i + 1].h() + when (e) { + is SafeInsetsElement -> { + y += placedInsetTop + leading = false + } + is PaddingElement -> { + x += d.px(e.start) + y += d.px(e.top) + leading = false + } + is OffsetElement -> { + if (leading) { + offsetX += d.px(e.x) + offsetY += d.px(e.y) + } else { + x += d.px(e.x) + y += d.px(e.y) + } + } + is WrapElement -> { + val o = e.align.align(androidx.compose.ui.unit.IntSize(nw, nh), + androidx.compose.ui.unit.IntSize(w, h), LTR) + x += o.x + y += o.y + leading = false + } + is ScrollElement -> leading = false + is LayoutElement -> { + // A box forced larger than its content centers it, as Compose does. + x += (w - nw) / 2 + y += (h - nh) / 2 + leading = false + } + else -> leading = false + } + } + val n = chain.size * 4 + contentX = boxes[n] + contentY = boxes[n + 1] + contentW = boxes[n + 2] + contentH = boxes[n + 3] + } + + /// Copies the positions the last measure recorded onto the children. + internal fun applyPlacements() { + val count = componentCount + for (k in 0 until count) { + val child = getComponentAt(k) + if (placements.size < (k + 1) * 4) { + break + } + var x = contentX + placements[k * 4] + var y = contentY + placements[k * 4 + 1] + if (child is UiNode) { + x += child.offsetX + y += child.offsetY + } + child.setX(x) + child.setY(y) + child.setWidth(placements[k * 4 + 2]) + child.setHeight(placements[k * 4 + 3]) + } + } + + /// The axis a node scrolls along by itself, without a scroll modifier: 1 for + /// vertical, 2 for horizontal. Lazy lists do. + internal open fun lazyAxis(): Int = 0 + + /// Measures again under the last constraints, at the same size, and places + /// the whole subtree now rather than on the next layout pass. + internal fun remeasureInPlace() { + val lc = lastConstraints ?: return + val w = measuredWidth + val h = measuredHeight + measured = false + measure(lc) + if (measuredWidth != w || measuredHeight != h) { + // The parent placed this node at the old size; only a full pass can move its siblings. + invalidateMeasure() + return + } + applyTree() + repaint() + } + + private fun applyTree() { + applyPlacements() + for (k in 0 until componentCount) { + val child = getComponentAt(k) + if (child is UiNode) { + child.applyTree() + } else if (child is Container) { + child.layoutContainer() + } + } + } + + internal fun layoutFromCn1() { + val w = width + val h = height + if (!measured || sizes[0].w() != w || sizes[0].h() != h) { + measure(Constraints.fixed(w, h)) + } + applyPlacements() + if (pendingScroll >= 0) { + val v = pendingScroll + pendingScroll = -1 + if (isScrollableY) cn1ScrollY(v) else cn1ScrollX(v) + } + } + + override fun calcScrollSize(): Dimension { + // The scrolled extent is the box just inside the scroll modifier. + var n = chain.size * 4 + for (i in chain.indices) { + if (chain[i] is ScrollElement) { + n = (i + 1) * 4 + break + } + } + return Dimension(Math.max(width, boxes[n] + boxes[n + 2]), Math.max(height, boxes[n + 1] + boxes[n + 3])) + } + + override fun calcPreferredSize(): Dimension { + val s = measure(Constraints(0, cn1DisplayWidth(), 0, Constraints.INF)) + return Dimension(s.w(), s.h()) + } + + // --- painting ----------------------------------------------------------- + + override fun paintBackground(g: Graphics) { + val old = g.alpha + if (alpha < 1f) { + g.concatenateAlpha(Math.round(alpha * 255)) + } + g.isAntiAliased = true + val ax = x + val ay = y + for (i in chain.indices) { + val e = chain[i] + if (e !is DrawElement) { + continue + } + val bx = ax + boxes[i * 4] + val by = ay + boxes[i * 4 + 1] + val bw = boxes[i * 4 + 2] + val bh = boxes[i * 4 + 3] + when (e) { + is BackgroundElement -> Painting.fill(g, e.shape, e.color, e.brush, bx, by, bw, bh) + is BorderElement -> Painting.stroke(g, e.shape, e.color, Density.px(e.width), bx, by, bw, bh) + is DrawBehindElement -> { + g.translate(bx, by) + try { + e.onDraw(Cn1DrawScope(g, Size(bw.toFloat(), bh.toFloat()))) + } finally { + g.translate(-bx, -by) + } + } + else -> {} + } + } + g.alpha = old + } + + override fun paint(g: Graphics) { + val old = g.alpha + if (alpha < 1f) { + g.concatenateAlpha(Math.round(alpha * 255)) + } + val clip = clipBox() + if (clip >= 0) { + g.pushClip() + g.clipRect(x + boxes[clip * 4], y + boxes[clip * 4 + 1], boxes[clip * 4 + 2], boxes[clip * 4 + 3]) + } + paintContent(g) + if (clip >= 0) { + g.popClip() + } + val click = clickable + if (pressed && click != null && click.indication) { + g.color = 0x000000 + g.concatenateAlpha(30) + val shape = indicationShape() + Painting.fill(g, shape, androidx.compose.ui.graphics.Color.Black, null, x, y, width, height) + } + g.alpha = old + } + + /// Paints what is inside the content box; containers paint their children. + protected open fun paintContent(g: Graphics) { + super.paint(g) + } + + private fun clipBox(): Int { + for (i in chain.indices) { + if (chain[i] is ClipElement) { + return i + } + } + return -1 + } + + private fun indicationShape(): Shape { + for (e in chain) { + if (e is ClipElement) return e.shape + if (e is BackgroundElement) return e.shape + } + return androidx.compose.ui.graphics.RectangleShape + } + + // --- input -------------------------------------------------------------- + + private var pressed = false + private var pressX = 0 + private var pressY = 0 + private var moved = false + + override fun pointerPressed(x: Int, y: Int) { + super.pointerPressed(x, y) + if (clickable?.enabled == true || tap != null) { + pressed = true + moved = false + pressX = x + pressY = y + repaint() + } + } + + override fun pointerDragged(x: Int, y: Int) { + if (pressed && !moved) { + val slop = Density.px(androidx.compose.ui.unit.Dp(8f)) + if (Math.abs(x - pressX) > slop || Math.abs(y - pressY) > slop) { + moved = true + pressed = false + repaint() + } + } + super.pointerDragged(x, y) + } + + override fun pointerReleased(x: Int, y: Int) { + super.pointerReleased(x, y) + if (pressed) { + pressed = false + repaint() + if (!moved && contains(x, y)) { + tap?.onTap?.invoke((x - absoluteX).toFloat(), (y - absoluteY).toFloat()) + val c = clickable + if (c != null && c.enabled) { + c.onClick() + } + } + } + } + + /// Clicks the node as a tap would, for tests and automation. + fun cn1PerformClick(): Boolean { + val c = clickable ?: return false + if (!c.enabled) return false + c.onClick() + return true + } + + internal fun cn1ScrollY(v: Int) { + scrollY = Math.max(0, Math.min(v, Math.max(0, scrollDimension.height - height))) + } + + private var pendingScroll = -1 + + /// A scroll position to restore once the node has been measured. + internal fun cn1ScrollPending(v: Int, vertical: Boolean) { + pendingScroll = v + } + + internal fun cn1ScrollX(v: Int) { + scrollX = Math.max(0, Math.min(v, Math.max(0, scrollDimension.width - width))) + } + + companion object { + private val EMPTY = arrayOf() + } +} + +/// Codename One's layout pass for a node: copies out what measuring decided. +internal object NodeLayout : Layout() { + override fun layoutContainer(parent: Container) { + // Checked, never cast: a failed cast does not throw on iOS. + (parent as? UiNode)?.layoutFromCn1() + } + + override fun getPreferredSize(parent: Container): Dimension { + val node = parent as? UiNode ?: return Dimension(0, 0) + val s = node.measure(Constraints(0, cn1DisplayWidth(), 0, Constraints.INF)) + return Dimension(s.w(), s.h()) + } +} + +internal fun cn1DisplayWidth(): Int = try { + com.codename1.ui.CN.getDisplayWidth().let { if (it > 0) it else 1080 } +} catch (t: Throwable) { + 1080 +} + +internal fun cn1DisplayHeight(): Int = try { + com.codename1.ui.CN.getDisplayHeight().let { if (it > 0) it else 1920 } +} catch (t: Throwable) { + 1920 +} + +/// Removes every theme decoration from a component that a node paints itself. +/// +/// A node is never focused, pressed or disabled, so only its unselected style is +/// ever read. Asking for `allStyles` created the other three, each with its own +/// background painter, for every node: 3,335 styles and 1 MB of a 150-card +/// screen's heap. A real input that takes focus passes `allStates`. +internal fun cn1StripStyle(c: Component, allStates: Boolean = false) { + val s = if (allStates) c.allStyles else c.unselectedStyle + s.setPadding(0, 0, 0, 0) + s.setMargin(0, 0, 0, 0) + s.setBgTransparency(0) + s.setBorder(Border.getEmpty()) +} + +/// Measures any child: a node by its own rules, any other Codename One component +/// by its preferred size, constrained. +internal fun measureChild(child: Component, c: Constraints): Long { + if (child is UiNode) { + return child.measure(c) + } + val pw = child.preferredW + val ph = child.preferredH + return size(c.constrainWidth(pw), c.constrainHeight(ph)) +} + +internal fun Outline.cornerArc(): Int = when (this) { + is Outline.Rounded -> Math.round(Math.max(Math.max(topLeft, topRight), Math.max(bottomLeft, bottomRight)) * 2) + else -> 0 +} diff --git a/maven/compose-compat/src/main/resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory b/maven/compose-compat/src/main/resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory new file mode 100644 index 00000000000..faf6dc9d6d0 --- /dev/null +++ b/maven/compose-compat/src/main/resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory @@ -0,0 +1 @@ +com.codename1.compose.internal.EdtDispatcherFactory diff --git a/maven/compose-compat/src/test/kotlin/com/codename1/compose/KeynoteAppTest.kt b/maven/compose-compat/src/test/kotlin/com/codename1/compose/KeynoteAppTest.kt new file mode 100644 index 00000000000..262fe0eb87b --- /dev/null +++ b/maven/compose-compat/src/test/kotlin/com/codename1/compose/KeynoteAppTest.kt @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose + +import com.codename1.compat.testing.HeadlessImplementation +import com.codename1.compat.testing.MainThreadRule +import androidx.compose.foundation.lazy.LazyListNode +import com.codename1.compose.internal.TextNode +import com.codename1.compose.internal.TextInputNode +import com.codename1.compose.internal.UiNode +import com.codename1.ui.Component +import com.codename1.ui.Container +import com.codename1.ui.Display +import keynote.App +import keynote.AppState +import keynote.Programme +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.BeforeClass +import org.junit.Rule +import org.junit.Test + +/// Drives the benchmark's hero application through every screen, headlessly. +class KeynoteAppTest { + @get:Rule + val mainThread = MainThreadRule() + + companion object { + @BeforeClass + @JvmStatic + fun install() { + HeadlessImplementation.install() + } + } + + private fun waitUntil(form: ComposeForm, what: String, cond: () -> Boolean) { + val end = System.currentTimeMillis() + 5000 + while (System.currentTimeMillis() < end) { + ComposeTesting.settle(form) + if (cond()) return + Display.getInstance().invokeAndBlock { Thread.sleep(10) } + } + throw AssertionError("timed out waiting for $what") + } + + private fun cards(root: Container): Int = count(root) { it.name?.startsWith("session-") == true } + + /// How many items the session list holds; only the ones in view are composed. + private fun items(form: ComposeForm): Int = (ComposeTesting.find(form, "list") as LazyListNode).cn1ItemCount() + + private fun count(c: Container, p: (Component) -> Boolean): Int { + var n = 0 + for (i in 0 until c.componentCount) { + val k = c.getComponentAt(i) + if (p(k)) n++ + if (k is Container) n += count(k, p) + } + return n + } + + private fun texts(c: Container, out: MutableList = ArrayList()): List { + for (i in 0 until c.componentCount) { + val k = c.getComponentAt(i) + if (k is TextNode) out.addAll(k.cn1Lines()) + if (k is Container) texts(k, out) + } + return out + } + + private fun click(form: ComposeForm, tag: String) { + val node = ComposeTesting.find(form, tag) as UiNode + assertTrue("$tag is not clickable", node.cn1PerformClick()) + ComposeTesting.settle(form) + } + + @Test + fun everyScreen() { + val state = AppState() + val form = ComposeTesting.show { App(state) } + waitUntil(form, "the programme to load") { cards(form) > 0 } + val day1 = Programme.sessions.filter { it.day == 1 } + assertEquals(day1.size, items(form)) + // Virtualized: the cards in view are composed, not the whole day. + val list = ComposeTesting.find(form, "list") as LazyListNode + assertTrue("composed ${cards(form)} of ${day1.size}", cards(form) < day1.size) + assertEquals(cards(form), list.cn1ComposedCount()) + // Scrolling to the end composes the last card and disposes of the first. + list.cn1ScrollY(Int.MAX_VALUE) + ComposeTesting.settle(form) + list.cn1ScrollY(Int.MAX_VALUE) + ComposeTesting.settle(form) + assertEquals(1, count(form) { it.name == "session-${day1.last().id}" }) + assertEquals(0, count(form) { it.name == "session-${day1.first().id}" }) + list.cn1ScrollY(0) + ComposeTesting.settle(form) + + // Favorite a session; it shows on the favorites tab. + click(form, "fav-0") + assertEquals(listOf(0), state.favorites.toList()) + click(form, "tab-favorites") + assertEquals(1, items(form)) + assertEquals(1, cards(form)) + click(form, "tab-schedule") + + // Day chips and search filter the list. + click(form, "day-2") + assertEquals(Programme.sessions.count { it.day == 2 }, items(form)) + val input = ComposeTesting.find(form, "search") + val field = findInput(input as Container)!! + field.field.text = "Compose" + ComposeTesting.settle(form) + assertEquals("Compose", state.query) + val expected = Programme.sessions.count { it.day == 2 && (it.title.contains("Compose", true) || + it.speaker.name.contains("Compose", true)) } + assertEquals(expected, items(form)) + + // Open a session and come back. + val first = Programme.sessions.first { it.day == 2 && it.title.contains("Compose", true) } + click(form, "session-${first.id}") + assertTrue(texts(form.contentPane).joinToString(" ").contains(first.speaker.name)) + click(form, "back") + assertNull(state.selected) + + // Insights: the chart animates in. + click(form, "tab-insights") + waitUntil(form, "the chart") { count(form) { it.name == "chart" } == 1 } + assertTrue(texts(form.contentPane).contains("You have 1 favorite sessions.")) + + // Paint every screen state once, to catch a painting failure. + val img = com.codename1.ui.Image.createImage(form.width, form.height) + form.paintComponent(img.graphics, true) + } + + private fun findInput(c: Container): TextInputNode? { + for (i in 0 until c.componentCount) { + val k = c.getComponentAt(i) + if (k is TextInputNode) return k + if (k is Container) findInput(k)?.let { return it } + } + return null + } +} diff --git a/maven/compose-compat/src/test/kotlin/com/codename1/compose/LayoutTest.kt b/maven/compose-compat/src/test/kotlin/com/codename1/compose/LayoutTest.kt new file mode 100644 index 00000000000..a87432c193b --- /dev/null +++ b/maven/compose-compat/src/test/kotlin/com/codename1/compose/LayoutTest.kt @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp +import com.codename1.compat.testing.HeadlessImplementation +import com.codename1.compat.testing.MainThreadRule +import com.codename1.ui.Component +import com.codename1.ui.Container +import com.codename1.ui.Display +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.BeforeClass +import org.junit.Rule +import org.junit.Test + +class LayoutTest { + @get:Rule + val mainThread = MainThreadRule() + + companion object { + @BeforeClass + @JvmStatic + fun install() { + HeadlessImplementation.install() + } + } + + @Test + fun columnRowAndWeights() { + val form = ComposeTesting.show { + Column(Modifier.fillMaxSize().padding(10.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) { + Box(Modifier.size(50.dp).background(Color.Red).testTag("a")) + Row(Modifier.fillMaxWidth().height(20.dp)) { + Box(Modifier.weight(1f).fillMaxSize().testTag("w1")) + Box(Modifier.width(30.dp).fillMaxSize().testTag("fixed")) + Box(Modifier.weight(2f).fillMaxSize().testTag("w2")) + } + Box(Modifier.fillMaxWidth().height(10.dp), contentAlignment = Alignment.Center) { + Spacer(Modifier.size(4.dp).testTag("centered")) + } + } + } + // Two pixels per dp under the headless implementation. + val a = ComposeTesting.find(form, "a") + assertEquals(20, a.absoluteX - form.contentPane.absoluteX) + assertEquals(100, a.width) + val w1 = ComposeTesting.find(form, "w1") + val fixed = ComposeTesting.find(form, "fixed") + val w2 = ComposeTesting.find(form, "w2") + assertEquals(60, fixed.width) + val total = form.contentPane.width - 40 + assertEquals(total - 60, w1.width + w2.width) + org.junit.Assert.assertTrue(Math.abs(w1.width * 2 - w2.width) <= 1) + assertEquals(40, w1.height) + assertEquals(a.absoluteY + 100 + 8, w1.absoluteY) + val c = ComposeTesting.find(form, "centered") + assertEquals(8, c.width) + assertEquals(w1.absoluteY + 40 + 8 + 6, c.absoluteY) + } + + @Test + fun clickRecomposes() { + var count by mutableIntStateOf(0) + val form = ComposeTesting.show { + Column { + BasicText("Count $count", Modifier.testTag("label")) + Box(Modifier.size(40.dp).clickable { count++ }.testTag("button")) + } + } + val label = ComposeTesting.find(form, "label") as com.codename1.compose.internal.TextNode + assertEquals(listOf("Count 0"), label.cn1Lines()) + val button = ComposeTesting.find(form, "button") as com.codename1.compose.internal.UiNode + button.cn1PerformClick() + ComposeTesting.settle(form) + assertEquals(listOf("Count 1"), label.cn1Lines()) + assertEquals(7 * HeadlessImplementation.CHAR_WIDTH, label.width) + } + + @Test + fun textWraps() { + val form = ComposeTesting.show { + Box(Modifier.width(50.dp)) { + BasicText("aaa bbb ccc", Modifier.testTag("t")) + } + } + val t = ComposeTesting.find(form, "t") as com.codename1.compose.internal.TextNode + assertEquals(listOf("aaa", "bbb", "ccc"), t.cn1Lines()) + assertEquals(3 * HeadlessImplementation.FONT_HEIGHT, t.height) + } +} + +/// Shows compositions and waits for them to settle. +object ComposeTesting { + fun show(content: @androidx.compose.runtime.Composable () -> Unit): ComposeForm { + val f = ComposeForm(content = content) + f.show() + settle(f) + return f + } + + fun settle(f: ComposeForm) { + for (i in 0 until 50) { + MainThreadRule.drain() + f.view.cn1Frame() + f.revalidate() + if (!f.view.hasPendingWork()) { + MainThreadRule.drain() + f.revalidate() + return + } + } + throw AssertionError("composition did not settle") + } + + fun find(root: Container, tag: String): Component { + val r = search(root, tag) + assertNotNull("no node tagged $tag", r) + return r!! + } + + private fun search(c: Container, tag: String): Component? { + for (i in 0 until c.componentCount) { + val k = c.getComponentAt(i) + if (tag == k.name) return k + if (k is Container) search(k, tag)?.let { return it } + } + return null + } +} diff --git a/maven/compose-compat/src/test/kotlin/com/codename1/compose/LazyListTest.kt b/maven/compose-compat/src/test/kotlin/com/codename1/compose/LazyListTest.kt new file mode 100644 index 00000000000..6f9dae6c451 --- /dev/null +++ b/maven/compose-compat/src/test/kotlin/com/codename1/compose/LazyListTest.kt @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.compose + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListNode +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp +import com.codename1.compat.testing.HeadlessImplementation +import com.codename1.compat.testing.MainThreadRule +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.BeforeClass +import org.junit.Rule +import org.junit.Test + +class LazyListTest { + @get:Rule + val mainThread = MainThreadRule() + + companion object { + @BeforeClass + @JvmStatic + fun install() { + HeadlessImplementation.install() + } + + var itemRuns = 0 + } + + private fun list(form: ComposeForm): LazyListNode = ComposeTesting.find(form, "list") as LazyListNode + + @Test + fun parentRecompositionDoesNotRecomposeItems() { + var count by mutableIntStateOf(0) + itemRuns = 0 + val form = ComposeTesting.show { + Column { + BasicText("Count $count", Modifier.testTag("label")) + LazyColumn(Modifier.height(300.dp).testTag("list")) { + items(100, key = { it }) { + itemRuns++ + BasicText("item $it", Modifier.height(40.dp)) + } + } + } + } + val composed = list(form).cn1ComposedCount() + assertTrue("only the items in view are composed", composed in 1 until 100) + assertEquals(composed, itemRuns) + count++ + ComposeTesting.settle(form) + val label = ComposeTesting.find(form, "label") as com.codename1.compose.internal.TextNode + assertEquals(listOf("Count 1"), label.cn1Lines()) + assertEquals("a recomposition above the list must not recompose its items", composed, itemRuns) + } + + @Test + fun duplicateKeysAreRefused() { + try { + ComposeTesting.show { + LazyColumn(Modifier.height(300.dp).testTag("list")) { + items(5, key = { "same" }) { BasicText("item $it", Modifier.height(40.dp)) } + } + } + fail("two items with one key must be refused") + } catch (e: IllegalArgumentException) { + assertTrue(e.message!!.contains("same")) + } + } + + @Test + fun scrollToItemLandsOnTheMeasuredItem() { + var state: LazyListState? = null + val form = ComposeTesting.show { + val s = rememberLazyListState() + state = s + // Sizes nothing like the estimate taken from the first items, so the target's + // position is only right once its neighbours have been measured. + LazyColumn(Modifier.height(300.dp).testTag("list"), state = s) { + items(200, key = { it }) { + BasicText("item $it", Modifier.fillMaxWidth().height(if (it < 10) 20.dp else (30 + (it % 7) * 15).dp) + .testTag("item-$it")) + } + } + } + val node = list(form) + for (target in intArrayOf(150, 60, 199, 3)) { + runBlocking { state!!.scrollToItem(target) } + ComposeTesting.settle(form) + val item = ComposeTesting.find(form, "item-$target") + // A container's absolute Y has its own scroll taken off; the view's top is this. + val top = node.absoluteY + node.scrollY + val want = if (target == 199) top + node.height - item.height else top + assertEquals("item $target starts where scrollToItem put it", want, item.absoluteY) + if (target != 199) assertEquals(target, state!!.firstVisibleItemIndex) + } + } + + @Test + fun aLongListCostsTheWindow() { + var state: LazyListState? = null + val form = ComposeTesting.show { + val s = rememberLazyListState() + state = s + LazyColumn(Modifier.height(300.dp).testTag("list"), state = s) { + items(100000, key = { it }) { + BasicText("item $it", Modifier.height((20 + (it % 5) * 10).dp).testTag("item-$it")) + } + } + } + val node = list(form) + var target = 0 + while (target < 100000) { + runBlocking { state!!.scrollToItem(target) } + ComposeTesting.settle(form) + assertTrue("composed ${node.cn1ComposedCount()}", node.cn1ComposedCount() < 40) + val item = ComposeTesting.find(form, "item-$target") + if (target < 99990) assertEquals(node.absoluteY + node.scrollY, item.absoluteY) + target += 9973 + } + } + + @Test + fun userScrollCanBeTurnedOff() { + val form = ComposeTesting.show { + LazyColumn(Modifier.height(300.dp).testTag("list"), userScrollEnabled = false) { + items(100, key = { it }) { BasicText("item $it", Modifier.height(40.dp)) } + } + } + assertFalse(list(form).isScrollableY) + } +} diff --git a/maven/core-unittests/spotbugs-exclude.xml b/maven/core-unittests/spotbugs-exclude.xml index bd949d9e26b..0afa8214ea0 100644 --- a/maven/core-unittests/spotbugs-exclude.xml +++ b/maven/core-unittests/spotbugs-exclude.xml @@ -369,6 +369,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/util/ThemeStringsTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/util/ThemeStringsTest.java new file mode 100644 index 00000000000..3c4345ac574 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/util/ThemeStringsTest.java @@ -0,0 +1,84 @@ +package com.codename1.ui.util; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ThemeStringsTest { + + @Test + void readUtfRejectsBadContinuationBytes() { + final byte[][] bad = {{0, 2, (byte) 0xc3, 0x41}, {0, 3, (byte) 0xe2, (byte) 0x82, 0x41}, {0, 1, (byte) 0x80}}; + for (final byte[] b : bad) { + assertThrows(IOException.class, () -> new ThemeStrings(1).readUTF(new DataInputStream(new ByteArrayInputStream(b)))); + } + } + + @Test + void hexAndDecimalMatchTheJdk() { + ThemeStrings s = new ThemeStrings(0); + int[] values = {0, 1, 9, 10, 15, 16, 0xff, 0x7aff, 0xffffff, 0x1000000, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}; + for (int v : values) { + assertEquals(Integer.toHexString(v), s.hex(v)); + } + for (int v = 0; v < 256; v++) { + assertEquals(String.valueOf(v), s.decimal(v)); + } + } + + @Test + void readUtfRoundTripsAndSharesRepeats() throws IOException { + String[] values = {"", "ffffff", "native:MainRegular", "caf\u00e9", "\u05e9\u05dc\u05d5\u05dd", "\u0000x", "ffffff"}; + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bo); + for (String v : values) { + out.writeUTF(v); + } + DataInputStream in = new DataInputStream(new ByteArrayInputStream(bo.toByteArray())); + ThemeStrings s = new ThemeStrings(0); + String[] read = new String[values.length]; + for (int i = 0; i < values.length; i++) { + read[i] = s.readUTF(in); + assertEquals(values[i], read[i]); + } + assertSame(read[1], read[6]); + assertSame(read[1], s.hex(0xffffff)); + assertSame(read[2], s.share(new String("native:MainRegular"))); + } + + @Test + void manyDistinctValuesSurviveGrowth() { + ThemeStrings s = new ThemeStrings(0); + String[] first = new String[5000]; + for (int i = 0; i < first.length; i++) { + first[i] = s.share("k" + i); + } + for (int i = 0; i < first.length; i++) { + assertSame(first[i], s.share("k" + i)); + } + } + + @Test + void floatsAndIntsMatchConcatenation() { + ThemeStrings s = new ThemeStrings(16); + for (int k = -20000; k <= 20000; k++) { + float f = (float) (k / 10.0); + assertEquals("" + f + "," + f + "," + f + "," + f, s.floats(f, f, f, f)); + } + float[] odd = {0.25f, 1.125f, 1e7f, 3.4e38f, -0.0f, 1e-5f, 0.001f, Float.NaN, Float.POSITIVE_INFINITY, 123456.7f, 999999.9f}; + for (float f : odd) { + assertEquals("" + f + "," + 0f + "," + f + "," + 1f, s.floats(f, 0f, f, 1f)); + } + for (int v = 0; v < 256; v++) { + assertEquals("" + v + "," + 255 + "," + 0 + "," + v, s.ints(v, 255, 0, v)); + } + } +} diff --git a/maven/css-compiler/src/main/java/com/codename1/ui/util/EditableResources.java b/maven/css-compiler/src/main/java/com/codename1/ui/util/EditableResources.java index f2dd663e152..91f48ba7cec 100644 --- a/maven/css-compiler/src/main/java/com/codename1/ui/util/EditableResources.java +++ b/maven/css-compiler/src/main/java/com/codename1/ui/util/EditableResources.java @@ -1724,14 +1724,14 @@ public long getDataSize(String id) { /** * Adapts loaded themes to the new resource selected/unselected modes */ - Hashtable loadTheme(String id, boolean newerVersion) throws IOException { + HashMap loadThemeMap(String id, boolean newerVersion) throws IOException { if(overrideResource != null) { - Hashtable h = overrideResource.loadTheme(id, newerVersion); + HashMap h = overrideResource.loadThemeMap(id, newerVersion); if(h != null) { return h; } } - Hashtable h = super.loadTheme(id, newerVersion); + HashMap h = super.loadThemeMap(id, newerVersion); Iterator keyIter = h.keySet().iterator(); while(keyIter.hasNext()) { diff --git a/maven/integration-tests/kmp-compat-test.sh b/maven/integration-tests/kmp-compat-test.sh new file mode 100755 index 00000000000..117c2297544 --- /dev/null +++ b/maven/integration-tests/kmp-compat-test.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Codename One designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Codename One through http://www.codenameone.com/ if you +# need additional information or have any questions. +# +# Kotlin Multiplatform sources as a Codename One application, end to end, for +# every sample under scripts/kmp-compat-samples: +# - cn1:import-kmp-project copies commonMain (and the JVM source set, minus +# its desktop window) into common/src/main/kmp and writes the main class; +# - the Maven build compiles them with the Compose compiler against +# compose-compat and passes the bytecode compliance check; +# - the Gradle conversion applies the Compose plugin, and both tools stage +# the same upload for every target, carrying the Compose runtime and +# compose-compat as they are -- androidx.compose is never relocated. +# +# Needs the reactor installed (mvn install) and a JDK 17+; see inc/gradle.sh. +SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" +set -e +SAMPLES="$SCRIPTPATH/../../scripts/kmp-compat-samples" +source "$SCRIPTPATH/inc/env.sh" +source "$SCRIPTPATH/inc/gradle.sh" +source "$SCRIPTPATH/inc/compat.sh" + +WORKDIR="${CN1_COMPAT_WORKDIR:-$SCRIPTPATH/build}/kmp-compat" +rm -rf "$WORKDIR" +mkdir -p "$WORKDIR" +FAILED=0 + +check_kmp_upload() { + local jar="$1" + assert_zip_has "$jar" "(^|/)$MAIN_ENTRY\\.class\$" + assert_zip_has "$jar" "^androidx/compose/runtime/Recomposer\\.class\$" + assert_zip_has "$jar" "^com/codename1/compose/ComposeView\\.class\$" + assert_zip_has "$jar" "^kotlinx/coroutines/.*\\.class\$" + # The dispatcher kotlinx.coroutines finds for Dispatchers.Main. + assert_zip_has "$jar" "^META-INF/services/kotlinx\\.coroutines\\.internal\\.MainDispatcherFactory\$" + assert_zip_lacks "$jar" "^com/codename1/androidcompat/androidx/compose/" +} + +for sample_dir in "$SAMPLES"/*/; do + sample=$(basename "$sample_dir") + echo "== $sample" + W="$WORKDIR/$sample" + mkdir -p "$W" + PKG=com.acme.kmp + compat_generate_app "$sample" "$W" $PKG KmpApp + MAPP="$W/app" + (cd "$MAPP" && mvn_local "com.codenameone:codenameone-maven-plugin:$CN1_VERSION:import-kmp-project" \ + "-Dcn1.kmp.import=$sample_dir") > "$W/import.log" 2>&1 \ + || { tail -40 "$W/import.log"; fail "$sample: import-kmp-project"; } + kmp="$MAPP/common/src/main/kmp" + [ -n "$(find "$kmp/commonMain" -name '*.kt' 2>/dev/null | head -1)" ] || fail "$sample: the import copied no commonMain sources" + [ -z "$(grep -rl 'androidx\.compose\.ui\.window' "$kmp" 2>/dev/null)" ] \ + || fail "$sample: the import kept a source that opens a desktop window" + grep -q 'left out: iosMain' "$W/import.log" || fail "$sample: the import did not report the iosMain source set" + MAIN_ENTRY=com/acme/kmp/KmpApp + + compat_build_common "$sample" "$W" "$MAPP" + grep -q 'bytecode-compliance' "$W/mvn-common.log" || fail "$sample: the compliance check did not run" + classes="$MAPP/common/target/classes" + [ -f "$classes/$MAIN_ENTRY.class" ] || fail "$sample: the build produced no $MAIN_ENTRY.class" + # The Compose compiler ran: a composable takes the Composer it added. + find "$classes" -name 'AppKt.class' -exec javap -p {} \; 2>/dev/null | grep -q 'androidx.compose.runtime.Composer' \ + || fail "$sample: App() was not compiled by the Compose compiler" + + compat_convert_to_gradle "$sample" "$W" "$MAPP" + GAPP="$W/app-gradle" + [ -d "$GAPP/src/main/kmp" ] || fail "$sample: the Gradle conversion dropped src/main/kmp" + grep -q 'org.jetbrains.kotlin.plugin.compose' "$GAPP/build.gradle.kts" \ + || fail "$sample: the Gradle conversion did not apply the Compose compiler plugin" + + compat_stage_all "$sample" "$W" "$MAPP" "$GAPP" check_kmp_upload +done + +[ "$FAILED" = 0 ] || { echo "kmp-compat-test: FAILED"; exit 1; } +echo "kmp-compat-test: OK" diff --git a/maven/pom.xml b/maven/pom.xml index 0e9749d93bf..7c3bea5cf4c 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -97,6 +97,8 @@ android-compat swing-compat javafx-compat + + compose-compat fxml-compiler lottie-transcoder + + compose + + + ${basedir}/src/main/kmp + + + + + + google + https://maven.google.com + + + + + com.codenameone + codenameone-compose-compat + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-compose-compiler-plugin-embeddable + ${kotlin.version} + + + + + + + @@ -148,7 +192,9 @@ - 1.6.0 + + 2.2.10 true @@ -163,7 +209,7 @@ org.jetbrains annotations - 13.0 + ${kotlin.annotations.version} com.codenameone @@ -212,6 +258,9 @@ ${android.gallery.dir}/kotlin ${android.gallery.dir}/java ${project.build.directory}/generated-sources/android + + ${project.basedir}/src/main/kmp/commonMain/kotlin + ${project.basedir}/src/main/kmp/jvmMain/kotlin -no-reflect diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index aba2a1209c4..c80ccc5d16b 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -58,6 +58,10 @@ import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatSwingTreesScreenshotTest; import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatSwingPaintingScreenshotTest; import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatSwingContainersScreenshotTest; +import com.codenameone.examples.hellocodenameone.tests.kmpcompat.KmpCompatLayoutScreenshotTest; +import com.codenameone.examples.hellocodenameone.tests.kmpcompat.KmpCompatTypographyScreenshotTest; +import com.codenameone.examples.hellocodenameone.tests.kmpcompat.KmpCompatListScreenshotTest; +import com.codenameone.examples.hellocodenameone.tests.kmpcompat.KmpCompatStateScreenshotTest; import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatSwingOptionPaneScreenshotTest; import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatSwingXScreenshotTest; import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatFxControlsScreenshotTest; @@ -707,6 +711,11 @@ private static int testTimeoutMs(BaseTest testClass) { new DesktopCompatFxFxmlScreenshotTest(), new DesktopCompatFxPanesScreenshotTest(), new DesktopCompatFxAlertScreenshotTest(), + // Compose (common/src/main/kmp), embedded through ComposeView. + new KmpCompatLayoutScreenshotTest(), + new KmpCompatTypographyScreenshotTest(), + new KmpCompatListScreenshotTest(), + new KmpCompatStateScreenshotTest(), // After every test that shows a dialog. It asks for the notification // permission, and on iOS the system alert nobody answers leaves the // application inactive for the rest of the run: Display.isMinimized() diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatLayoutScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatLayoutScreenshotTest.java new file mode 100644 index 00000000000..db42bbcb5fe --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatLayoutScreenshotTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.tests.kmpcompat; + +import com.codename1.ui.Component; +import com.codenameone.examples.hellocodenameone.kmp.KmpScenes; +import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatScreenshotTest; + +/// A Compose screen of common/src/main/kmp, embedded as a component. +public class KmpCompatLayoutScreenshotTest extends DesktopCompatScreenshotTest { + public KmpCompatLayoutScreenshotTest() { + super("Compose layout", "KmpCompatLayout"); + } + + @Override + protected Component scene() { + return KmpScenes.layout(); + } +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatListScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatListScreenshotTest.java new file mode 100644 index 00000000000..a9c411b8e7d --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatListScreenshotTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.tests.kmpcompat; + +import com.codename1.ui.Component; +import com.codenameone.examples.hellocodenameone.kmp.KmpScenes; +import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatScreenshotTest; + +/// A Compose screen of common/src/main/kmp, embedded as a component. +public class KmpCompatListScreenshotTest extends DesktopCompatScreenshotTest { + public KmpCompatListScreenshotTest() { + super("Compose list", "KmpCompatList"); + } + + @Override + protected Component scene() { + return KmpScenes.list(); + } +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatStateScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatStateScreenshotTest.java new file mode 100644 index 00000000000..0831fa13bd3 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatStateScreenshotTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.tests.kmpcompat; + +import com.codename1.ui.Component; +import com.codenameone.examples.hellocodenameone.kmp.KmpScenes; +import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatScreenshotTest; + +/// A Compose screen of common/src/main/kmp, embedded as a component. +public class KmpCompatStateScreenshotTest extends DesktopCompatScreenshotTest { + public KmpCompatStateScreenshotTest() { + super("Compose state", "KmpCompatState"); + } + + @Override + protected Component scene() { + return KmpScenes.state(); + } +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatTypographyScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatTypographyScreenshotTest.java new file mode 100644 index 00000000000..5288d27e210 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/kmpcompat/KmpCompatTypographyScreenshotTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.tests.kmpcompat; + +import com.codename1.ui.Component; +import com.codenameone.examples.hellocodenameone.kmp.KmpScenes; +import com.codenameone.examples.hellocodenameone.tests.desktopcompat.DesktopCompatScreenshotTest; + +/// A Compose screen of common/src/main/kmp, embedded as a component. +public class KmpCompatTypographyScreenshotTest extends DesktopCompatScreenshotTest { + public KmpCompatTypographyScreenshotTest() { + super("Compose typography", "KmpCompatTypography"); + } + + @Override + protected Component scene() { + return KmpScenes.typography(); + } +} diff --git a/scripts/hellocodenameone/common/src/main/kmp/commonMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/Screens.kt b/scripts/hellocodenameone/common/src/main/kmp/commonMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/Screens.kt new file mode 100644 index 00000000000..fa9b30f203e --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/kmp/commonMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/Screens.kt @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.kmp + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp + +/// Box, Row and Column under padding, size, background, border and weight. +@Composable +fun LayoutScreen() { + MaterialTheme { + Column(Modifier.fillMaxSize().padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Box(Modifier.weight(1f).height(48.dp).background(Color(0xFFE53935))) + Box(Modifier.weight(2f).height(48.dp).background(Color(0xFF43A047))) + Box(Modifier.size(48.dp).background(Color(0xFF1E88E5), RoundedCornerShape(12.dp))) + } + Box(Modifier.fillMaxWidth().height(96.dp).border(2.dp, Color(0xFF6750A4), RoundedCornerShape(8.dp)), + contentAlignment = Alignment.Center) { + Text("Centered in a bordered box") + } + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + Box(Modifier.size(32.dp).background(Color(0xFFFFB300))) + Spacer(Modifier.size(12.dp)) + Column { + Text("Row with a spacer", fontWeight = FontWeight.Bold) + Text("and a nested column") + } + } + } + } +} + +/// The Material 3 type scale and buttons. +@Composable +fun TypographyScreen() { + MaterialTheme { + Column(Modifier.fillMaxSize().padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text("Headline large", style = MaterialTheme.typography.headlineLarge) + Text("Title medium", style = MaterialTheme.typography.titleMedium) + Text("Body medium text that is long enough to wrap onto a second line in a phone width column.", + style = MaterialTheme.typography.bodyMedium) + Text("Label small", style = MaterialTheme.typography.labelSmall) + HorizontalDivider() + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Button(onClick = {}) { Text("Filled") } + OutlinedButton(onClick = {}) { Text("Outlined") } + TextButton(onClick = {}) { Text("Text") } + } + } + } +} + +/// A Scaffold with a top app bar over a LazyColumn of cards. +@Composable +fun ListScreen() { + MaterialTheme { + Scaffold(topBar = { TopAppBar(title = { Text("Compose list") }) }) { padding -> + LazyColumn(Modifier.fillMaxSize().padding(padding)) { + items(30) { index -> + Card(Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 6.dp)) { + Column(Modifier.padding(12.dp)) { + Text("Item $index", style = MaterialTheme.typography.titleMedium) + Text("Supporting text for row $index") + } + } + } + } + } + } +} + +/// State and recomposition: a counter bumped from an effect, and an +/// AnimatedVisibility captured at its end state. +@Composable +fun StateScreen() { + MaterialTheme { + var count by remember { mutableIntStateOf(0) } + var shown by remember { mutableStateOf(false) } + LaunchedEffect(Unit) { + count = 3 + shown = true + } + Column(Modifier.fillMaxSize().padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp)) { + Text("Clicked $count times", style = MaterialTheme.typography.headlineSmall) + Button(onClick = { count++ }) { Text("Click me") } + AnimatedVisibility(visible = shown) { + Card(Modifier.fillMaxWidth()) { + Text("Revealed by AnimatedVisibility", Modifier.padding(16.dp)) + } + } + AnimatedVisibility(visible = !shown) { + Text("This text is hidden at the end state") + } + } + } +} diff --git a/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/KmpScenes.kt b/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/KmpScenes.kt new file mode 100644 index 00000000000..ff40f45cedd --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/KmpScenes.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.kmp + +import com.codename1.compose.ComposeView +import com.codename1.ui.Component + +/// The Compose screens as Codename One components, for the KmpCompat +/// screenshot tests, which are Java. +object KmpScenes { + @JvmStatic fun layout(): Component = ComposeView { LayoutScreen() } + @JvmStatic fun typography(): Component = ComposeView { TypographyScreen() } + @JvmStatic fun list(): Component = ComposeView { ListScreen() } + @JvmStatic fun state(): Component = ComposeView { StateScreen() } +} diff --git a/scripts/hellocodenameone/pom.xml b/scripts/hellocodenameone/pom.xml index 77cc3585098..8910a17c372 100644 --- a/scripts/hellocodenameone/pom.xml +++ b/scripts/hellocodenameone/pom.xml @@ -31,6 +31,8 @@ 17 17 17 + + 23.0.0 hellocodenameone @@ -70,6 +72,13 @@ ${cn1.version} provided + + + com.codenameone + codenameone-compose-compat + ${cn1.version} + com.codenameone codenameone-unity-compat diff --git a/scripts/kmp-bench/README.md b/scripts/kmp-bench/README.md new file mode 100644 index 00000000000..0caa7dd4dbb --- /dev/null +++ b/scripts/kmp-bench/README.md @@ -0,0 +1,85 @@ +# Codename One vs. Compose Multiplatform + +One Compose application, **Keynote** (a conference schedule: search, day +filters, a 150-session list of cards, favorites, a detail screen and an animated +chart), built twice from the **same `commonMain` source** and run side by side: + +- **Codename One**: the source imported into an archetype application + (`common/src/main/kmp`), compiled by kotlinc with the Compose compiler against + `maven/compose-compat`, translated by ParparVM to C, built by Xcode. +- **Compose Multiplatform**: the same source as a CMP module + (`org.jetbrains.compose`, Kotlin/Native and Skia), linked as an iOS + executable. + +The shared code is in `app/shared/commonMain`. The only per-side files are each +side's entry point (`app/cn1/BenchEntry.kt`, `app/cmp/composeApp/src/iosMain`). +The CMP build adds one compiler opt-in, `ExperimentalMaterial3Api`, which the +real material3 demands for the top app bar. That keeps the shared source +identical on both sides. Keynote is written for this benchmark. It is not a +JetBrains sample. + +## Running it + +```bash +export JAVA17_HOME=... # JDK 17: Gradle, kotlinc, the CN1 translation +scripts/kmp-bench/run_bench.sh --work /tmp/kbench --runs 5 \ + --maven-repo "$PWD/.m2-repo" # the repository the checkout installed into +``` + +The script needs Xcode, Gradle 8.5 or newer on `PATH`, and the reactor installed +into the repository it is given. It boots one iPhone simulator, shuts it down +when it finishes, and writes `results.json`, `results.md` and a screenshot of +each app into the work directory. + +## What is measured + +| | | +|---|---| +| Start-up | From `simctl launch` to the app's `KMPBENCH first-frame` line. Both print it on the first main-thread turn after their UI is installed. CMP's marker fires before Skia has drawn anything, so the comparison favours CMP. Run 1 is the first launch after install (cold); the median of the rest is reported as warm. | +| Executable | The app binary after `strip`, which is what an App Store build ships. | +| Bundle | The `.app` directory as built (the CN1 binary in it is not yet stripped). | +| Memory | Physical footprint (`footprint `) five seconds after launch: the dirty memory the process owns, which is what iOS charges an app and kills it for. Resident size is not used: on the simulator it counts the shared dyld cache, which every process maps, so an empty app already shows 184 MB. | + +## Results + +iPhone 17 Pro simulator, Xcode 27, Apple silicon, Release builds, 5 runs. +Start-up here is from process start (the kernel's start time) to the marker, so +`simctl`'s own overhead is left out of both. + +| | Empty UIKit app | Codename One | Compose Multiplatform | +|---|---|---|---| +| Start-up | 640-750 ms | 596-685 ms | 646-794 ms | +| Executable, stripped | | 16.4 MB | 22.9 MB | +| App bundle | | 27.3 MB | 28.8 MB | +| Physical footprint | 34 MB | 47-48 MB | 48 MB | + +**Start-up is the simulator's floor.** An empty UIKit application, with no +code of its own, takes as long to show its first frame as either side, so +neither has much start-up of its own left to lose there. Measure on a device +before quoting a difference. + +The Codename One executable includes the whole iOS port and the full Material +icon font, so the 6.5 MB difference is a floor. + +**Memory is level.** The Java heap holds +3.2 MB live after start-up, in about 57,000 objects. Loading the theme used to +leave 24,000 strings behind -- one per key and value, plus every colour and +padding built by concatenation, and a cache key per `Font.derive` call. The +theme loader now decodes into reused buffers and shares repeated values +(`ThemeStrings`), and a derived font is found without building a key, which +took 0.6 MB of live heap and about 2 MB of footprint off. Freed pages stay +resident on iOS, so collecting sooner does not help: garbage that is never +created is the only saving. Most of what remains is per thread (two +`Dispatchers.Default` workers that `flowOn` starts) and text rendering (the +glyph atlas and CoreText). + +Codename One also targets platforms CMP does not: watchOS, tvOS, and native +Windows and Linux binaries with no JVM. Keynote has not been built for those +here yet. + +## Not yet covered + +- `LazyColumn` and `LazyRow` with `reverseLayout = true` are refused at run + time. The lists compose only the items in view. +- Frame time while scrolling is not measured. That needs a device and a driven + gesture rather than a simulator launch. diff --git a/scripts/kmp-bench/app/cmp/Info.plist b/scripts/kmp-bench/app/cmp/Info.plist new file mode 100644 index 00000000000..8b860c21dbd --- /dev/null +++ b/scripts/kmp-bench/app/cmp/Info.plist @@ -0,0 +1,14 @@ + + + +CFBundleExecutableKeynote +CFBundleIdentifiercom.example.kmpbench.cmp +CFBundleNameKeynote +CFBundlePackageTypeAPPL +CFBundleShortVersionString1.0 +CFBundleVersion1 +MinimumOSVersion15.0 +UIDeviceFamily1 +UILaunchScreen +CADisableMinimumFrameDurationOnPhone + diff --git a/scripts/kmp-bench/app/cmp/build.gradle.kts b/scripts/kmp-bench/app/cmp/build.gradle.kts new file mode 100644 index 00000000000..a1e59554fcf --- /dev/null +++ b/scripts/kmp-bench/app/cmp/build.gradle.kts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +plugins { + kotlin("multiplatform") version "2.2.10" apply false + id("org.jetbrains.kotlin.plugin.compose") version "2.2.10" apply false + id("org.jetbrains.compose") version "1.9.0" apply false +} diff --git a/scripts/kmp-bench/app/cmp/composeApp/build.gradle.kts b/scripts/kmp-bench/app/cmp/composeApp/build.gradle.kts new file mode 100644 index 00000000000..83278771e26 --- /dev/null +++ b/scripts/kmp-bench/app/cmp/composeApp/build.gradle.kts @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +// The Compose Multiplatform build of the same commonMain the Codename One +// project compiles. Release builds only: the benchmark measures what ships. +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType + +plugins { + kotlin("multiplatform") + id("org.jetbrains.kotlin.plugin.compose") + id("org.jetbrains.compose") +} + +kotlin { + jvm("desktop") + listOf(iosSimulatorArm64(), iosArm64()).forEach { target -> + target.binaries.executable(listOf(NativeBuildType.RELEASE)) { + entryPoint = "keynote.ios.main" + } + } + // The shared code is unchanged from the Codename One side, which needs no + // opt-in for the material3 top bar. + compilerOptions { + optIn.add("androidx.compose.material3.ExperimentalMaterial3Api") + } + sourceSets { + commonMain.dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + implementation(compose.materialIconsExtended) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") + } + val desktopMain by getting { + dependencies { + implementation(compose.desktop.currentOs) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.10.2") + } + } + } +} + +compose.desktop { + application { + mainClass = "keynote.desktop.MainKt" + } +} diff --git a/scripts/kmp-bench/app/cmp/composeApp/src/desktopMain/kotlin/Main.kt b/scripts/kmp-bench/app/cmp/composeApp/src/desktopMain/kotlin/Main.kt new file mode 100644 index 00000000000..e61e93ebf84 --- /dev/null +++ b/scripts/kmp-bench/app/cmp/composeApp/src/desktopMain/kotlin/Main.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package keynote.desktop + +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import androidx.compose.ui.window.rememberWindowState +import keynote.App + +fun main() = application { + Window(onCloseRequest = ::exitApplication, title = "Keynote", + state = rememberWindowState(width = 412.dp, height = 892.dp)) { + App() + } +} diff --git a/scripts/kmp-bench/app/cmp/composeApp/src/iosMain/kotlin/Main.kt b/scripts/kmp-bench/app/cmp/composeApp/src/iosMain/kotlin/Main.kt new file mode 100644 index 00000000000..020d310ab39 --- /dev/null +++ b/scripts/kmp-bench/app/cmp/composeApp/src/iosMain/kotlin/Main.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package keynote.ios + +import androidx.compose.ui.window.ComposeUIViewController +import keynote.App +import kotlinx.cinterop.BetaInteropApi +import kotlinx.cinterop.ExperimentalForeignApi +import platform.Foundation.NSDate +import platform.Foundation.NSStringFromClass +import platform.Foundation.timeIntervalSince1970 +import platform.UIKit.UIApplication +import platform.UIKit.UIApplicationDelegateProtocol +import platform.UIKit.UIApplicationDelegateProtocolMeta +import platform.UIKit.UIApplicationMain +import platform.UIKit.UIResponder +import platform.UIKit.UIResponderMeta +import platform.UIKit.UIScreen +import platform.UIKit.UIWindow +import platform.darwin.dispatch_async +import platform.darwin.dispatch_get_main_queue +import kotlinx.cinterop.autoreleasepool +import kotlinx.cinterop.cstr +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.toCValues + +/// The whole iOS application: a delegate whose window shows the composition. +/// An executable rather than a framework inside an Xcode project, so the build +/// needs no project file; the .app is assembled by build_apps.sh. +@OptIn(BetaInteropApi::class, ExperimentalForeignApi::class) +class AppDelegate @OverrideInit constructor() : UIResponder(), UIApplicationDelegateProtocol { + companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta + + private var mainWindow: UIWindow? = null + + override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map?): Boolean { + val w = UIWindow(frame = UIScreen.mainScreen.bounds) + w.rootViewController = ComposeUIViewController { App() } + w.makeKeyAndVisible() + mainWindow = w + // After the launch method returns, as the Codename One side prints its + // marker from callSerially once its form is shown. + dispatch_async(dispatch_get_main_queue()) { + println("KMPBENCH first-frame " + (NSDate().timeIntervalSince1970 * 1000.0).toLong()) + } + return true + } +} + +@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) +fun main(args: Array) { + memScoped { + val argv = (arrayOf("keynote") + args).map { it.cstr.ptr }.toCValues() + autoreleasepool { + UIApplicationMain(args.size + 1, argv, null, NSStringFromClass(AppDelegate)) + } + } +} diff --git a/scripts/kmp-bench/app/cmp/gradle.properties b/scripts/kmp-bench/app/cmp/gradle.properties new file mode 100644 index 00000000000..e36d638b6e3 --- /dev/null +++ b/scripts/kmp-bench/app/cmp/gradle.properties @@ -0,0 +1,3 @@ +kotlin.code.style=official +org.gradle.jvmargs=-Xmx3g +kotlin.native.ignoreDisabledTargets=true diff --git a/scripts/kmp-bench/app/cmp/settings.gradle.kts b/scripts/kmp-bench/app/cmp/settings.gradle.kts new file mode 100644 index 00000000000..cd383448492 --- /dev/null +++ b/scripts/kmp-bench/app/cmp/settings.gradle.kts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +rootProject.name = "keynote-cmp" +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + google() + } +} +dependencyResolutionManagement { + repositories { + mavenCentral() + google() + } +} +include(":composeApp") diff --git a/scripts/kmp-bench/app/cn1/Bench.java b/scripts/kmp-bench/app/cn1/Bench.java new file mode 100644 index 00000000000..810cc3b0c9e --- /dev/null +++ b/scripts/kmp-bench/app/cn1/Bench.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.example.kmpbench; + +import com.codename1.system.Lifecycle; + +/** + * Boots the Keynote hero application, the same Compose source the Compose + * Multiplatform side builds. All the work is in BenchEntry.kt: Compose content + * has to be created from Kotlin, where the compiler plugin makes the lambda + * composable. + */ +public class Bench extends Lifecycle { + @Override + public void runApp() { + BenchEntryKt.start(); + } +} diff --git a/scripts/kmp-bench/app/cn1/BenchEntry.kt b/scripts/kmp-bench/app/cn1/BenchEntry.kt new file mode 100644 index 00000000000..6044aee312a --- /dev/null +++ b/scripts/kmp-bench/app/cn1/BenchEntry.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.example.kmpbench + +import com.codename1.compose.ComposeForm +import com.codename1.ui.CN +import keynote.App + +/// Shows the application and prints the marker the harness times start-up +/// against, once the first frame with content has been painted. +fun start() { + val form = ComposeForm { App() } + form.show() + CN.callSerially { + println("KMPBENCH first-frame " + System.currentTimeMillis()) + } +} diff --git a/scripts/kmp-bench/app/prepare.sh b/scripts/kmp-bench/app/prepare.sh new file mode 100755 index 00000000000..346bacfd1a3 --- /dev/null +++ b/scripts/kmp-bench/app/prepare.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# Materialises BOTH sides of the Kotlin Multiplatform benchmark into a work +# directory, from ONE copy of the application. +# +# scripts/kmp-bench/app/prepare.sh --work /tmp/kbench [--maven-repo DIR] +# +# shared/commonMain is the application: plain Compose Multiplatform code with +# no Codename One import in it. Both projects compile that directory as their +# commonMain, so an edit cannot land on one side only. +# +# Outputs: +# /cn1/ a Codename One project generated from the shipping archetype, +# with the source in common/src/main/kmp (the compose profile) +# /cmp/ a JetBrains Compose Multiplatform project (desktop and iOS) +set -Eeuo pipefail +trap 'rc=$?; echo "prepare.sh: FAILED at line $LINENO (exit $rc)" >&2' ERR + +WORK="" +CN1_VERSION="${CN1_VERSION:-}" +MAVEN_REPO_LOCAL="${MAVEN_REPO_LOCAL:-}" +while [ $# -gt 0 ]; do + case "$1" in + --work) WORK="$2"; shift 2 ;; + --cn1-version) CN1_VERSION="$2"; shift 2 ;; + --maven-repo) MAVEN_REPO_LOCAL="$2"; shift 2 ;; + *) echo "unknown argument: $1" >&2; exit 2 ;; + esac +done +[ -n "$WORK" ] || { echo "usage: prepare.sh --work

" >&2; exit 2; } +HERE="$(cd "$(dirname "$0")" && pwd)" +REPO="$(cd "$HERE/../../.." && pwd)" +if [ -z "$CN1_VERSION" ]; then + CN1_VERSION="$(sed -n 's:.*\(.*-SNAPSHOT\).*:\1:p' "$REPO/maven/pom.xml" | head -1)" +fi +MVN_REPO_ARG="" +[ -n "$MAVEN_REPO_LOCAL" ] && MVN_REPO_ARG="-Dmaven.repo.local=$MAVEN_REPO_LOCAL" + +rm -rf "$WORK/cn1" "$WORK/cmp" +mkdir -p "$WORK" + +# --- Codename One ----------------------------------------------------------- + +( cd "$WORK" && mvn -B -q $MVN_REPO_ARG archetype:generate \ + -DarchetypeArtifactId=cn1app-archetype -DarchetypeGroupId=com.codenameone \ + -DarchetypeVersion="$CN1_VERSION" -DgroupId=com.example.kmpbench -DartifactId=cn1 \ + -Dversion=1.0-SNAPSHOT -DmainName=Bench -DinteractiveMode=false ) +COMMON="$WORK/cn1/common" +cp "$HERE/cn1/Bench.java" "$COMMON/src/main/java/com/example/kmpbench/Bench.java" +mkdir -p "$COMMON/src/main/kotlin/com/example/kmpbench" "$COMMON/src/main/kmp/commonMain" +cp "$HERE/cn1/BenchEntry.kt" "$COMMON/src/main/kotlin/com/example/kmpbench/" +cp -R "$HERE/shared/commonMain/kotlin" "$COMMON/src/main/kmp/commonMain/kotlin" +echo "codename1.arg.ios.newStorageLocation=true" >> "$COMMON/codenameone_settings.properties" + +# --- Compose Multiplatform -------------------------------------------------- + +cp -R "$HERE/cmp/." "$WORK/cmp/" +mkdir -p "$WORK/cmp/composeApp/src" +cp -R "$HERE/shared/commonMain" "$WORK/cmp/composeApp/src/commonMain" +echo "prepared $WORK (cn1 $CN1_VERSION)" diff --git a/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/App.kt b/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/App.kt new file mode 100644 index 00000000000..e82b964666f --- /dev/null +++ b/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/App.kt @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package keynote + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.DateRange +import androidx.compose.material.icons.filled.Favorite +import androidx.compose.material.icons.filled.FavoriteBorder +import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.Place +import androidx.compose.material.icons.filled.Schedule +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.FilterChip +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp + +/// What the application shows, hoisted so the benchmark can drive it. +class AppState { + var tab by mutableIntStateOf(0) + var day by mutableIntStateOf(1) + var query by mutableStateOf("") + var selected by mutableStateOf(null) + val favorites = mutableStateListOf() + + fun toggleFavorite(id: Int) { + if (!favorites.remove(id)) favorites.add(id) + } +} + +private val Brand = Color(0xFF7F52FF) + +@Composable +fun App(state: AppState = remember { AppState() }) { + val dark = isSystemInDarkTheme() + val scheme = if (dark) darkColorScheme(primary = Color(0xFFB9A4FF), secondaryContainer = Color(0xFF3A2D6B)) + else lightColorScheme(primary = Brand, secondaryContainer = Color(0xFFE9E1FF)) + MaterialTheme(colorScheme = scheme) { + val sessions by remember { Programme.load() }.collectAsState(emptyList()) + val selected = state.selected + if (selected != null) { + SessionDetail(selected, state.favorites.contains(selected.id), { state.toggleFavorite(selected.id) }) { + state.selected = null + } + return@MaterialTheme + } + Scaffold( + topBar = { + CenterAlignedTopAppBar(title = { Text(if (state.tab == 2) "Insights" else "KotlinConf-ish 2026", + fontWeight = FontWeight.SemiBold) }) + }, + bottomBar = { + NavigationBar { + NavigationBarItem(state.tab == 0, { state.tab = 0 }, { Icon(Icons.Default.DateRange, null) }, + label = { Text("Schedule") }, modifier = Modifier.testTag("tab-schedule")) + NavigationBarItem(state.tab == 1, { state.tab = 1 }, { Icon(Icons.Default.Favorite, null) }, + label = { Text("Favorites") }, modifier = Modifier.testTag("tab-favorites")) + NavigationBarItem(state.tab == 2, { state.tab = 2 }, { Icon(Icons.Default.Info, null) }, + label = { Text("Insights") }, modifier = Modifier.testTag("tab-insights")) + } + } + ) { padding -> + Box(Modifier.padding(padding).fillMaxSize()) { + if (sessions.isEmpty()) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } + } else when (state.tab) { + 0 -> Schedule(sessions, state) + 1 -> SessionList(sessions.filter { state.favorites.contains(it.id) }, state, + empty = "Tap the heart on a session to keep it here.") + else -> Insights(sessions, state) + } + } + } + } +} + +@Composable +private fun Schedule(sessions: List, state: AppState) { + Column(Modifier.fillMaxSize()) { + OutlinedTextField(state.query, { state.query = it }, Modifier.fillMaxWidth().padding(16.dp, 8.dp).testTag("search"), + placeholder = { Text("Search talks and speakers") }, leadingIcon = { Icon(Icons.Default.Search, null) }, + singleLine = true, shape = RoundedCornerShape(28.dp)) + LazyRow(contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) { + items(listOf(1, 2, 3)) { d -> + FilterChip(state.day == d, { state.day = d }, { Text("Day $d") }, Modifier.testTag("day-$d")) + } + } + val q = state.query.trim() + val visible = sessions.filter { + it.day == state.day && (q.isEmpty() || it.title.contains(q, ignoreCase = true) || + it.speaker.name.contains(q, ignoreCase = true)) + } + SessionList(visible, state, empty = "No session matches \"$q\".") + } +} + +@Composable +private fun SessionList(sessions: List, state: AppState, empty: String) { + if (sessions.isEmpty()) { + Box(Modifier.fillMaxSize().padding(32.dp), contentAlignment = Alignment.Center) { + Text(empty, color = MaterialTheme.colorScheme.onSurfaceVariant) + } + return + } + LazyColumn(Modifier.fillMaxSize().testTag("list"), contentPadding = PaddingValues(16.dp, 8.dp), + verticalArrangement = Arrangement.spacedBy(12.dp)) { + items(sessions, key = { it.id }) { s -> + SessionCard(s, state.favorites.contains(s.id), { state.toggleFavorite(s.id) }) { state.selected = s } + } + } +} + +@Composable +fun SessionCard(s: Session, favorite: Boolean, onFavorite: () -> Unit, onOpen: () -> Unit) { + Card(onOpen, Modifier.fillMaxWidth().testTag("session-${s.id}"), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLow)) { + Row(Modifier.padding(start = 0.dp, end = 4.dp), verticalAlignment = Alignment.CenterVertically) { + Box(Modifier.width(6.dp).height(84.dp).background(Color(s.track.color))) + Column(Modifier.weight(1f).padding(12.dp)) { + Text(s.timeLabel + " - " + s.room, style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant) + Spacer(Modifier.height(4.dp)) + Text(s.title, style = MaterialTheme.typography.titleMedium, maxLines = 2, overflow = TextOverflow.Ellipsis) + Spacer(Modifier.height(2.dp)) + Text(s.speaker.name + ", " + s.speaker.company, style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant) + } + FavoriteButton(favorite, onFavorite, Modifier.testTag("fav-${s.id}")) + } + } +} + +@Composable +private fun FavoriteButton(favorite: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) { + val scale by animateFloatAsState(if (favorite) 1f else 0.8f) + IconButton(onClick, modifier) { + Icon(if (favorite) Icons.Default.Favorite else Icons.Default.FavoriteBorder, + if (favorite) "Remove from favorites" else "Add to favorites", + Modifier.size((24 * scale).dp), + tint = if (favorite) Color(0xFFE91E63) else MaterialTheme.colorScheme.onSurfaceVariant) + } +} + +@Composable +private fun SessionDetail(s: Session, favorite: Boolean, onFavorite: () -> Unit, onBack: () -> Unit) { + Scaffold(topBar = { + CenterAlignedTopAppBar(title = { Text(s.track.label) }, + navigationIcon = { IconButton(onBack, Modifier.testTag("back")) { Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back") } }, + actions = { FavoriteButton(favorite, onFavorite) }) + }) { padding -> + Column(Modifier.padding(padding).padding(20.dp).fillMaxWidth()) { + Text(s.title, style = MaterialTheme.typography.headlineSmall) + Spacer(Modifier.height(16.dp)) + Row(verticalAlignment = Alignment.CenterVertically) { + Box(Modifier.size(48.dp).background(Color(s.track.color), CircleShape), contentAlignment = Alignment.Center) { + Text(s.speaker.name.split(" ").joinToString("") { it.take(1) }, color = Color.White, + fontWeight = FontWeight.Bold) + } + Spacer(Modifier.width(12.dp)) + Column { + Text(s.speaker.name, style = MaterialTheme.typography.titleMedium) + Text(s.speaker.company, style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant) + } + } + Spacer(Modifier.height(16.dp)) + HorizontalDivider() + Spacer(Modifier.height(16.dp)) + DetailLine(Icons.Default.Schedule, "Day ${s.day}, ${s.timeLabel}") + DetailLine(Icons.Default.Place, s.room) + Spacer(Modifier.height(16.dp)) + Text(s.description, style = MaterialTheme.typography.bodyLarge) + } + } +} + +@Composable +private fun DetailLine(icon: androidx.compose.ui.graphics.vector.ImageVector, text: String) { + Row(Modifier.padding(vertical = 4.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(icon, null, tint = MaterialTheme.colorScheme.primary) + Spacer(Modifier.width(12.dp)) + Text(text, style = MaterialTheme.typography.bodyLarge) + } +} + +/// Sessions per track, as bars that grow in when the tab opens. +@Composable +private fun Insights(sessions: List, state: AppState) { + var shown by remember { mutableStateOf(false) } + LaunchedEffect(Unit) { shown = true } + val grow by animateFloatAsState(if (shown) 1f else 0f, tween(700)) + val counts = Track.entries.map { t -> t to sessions.count { it.track == t && it.day == state.day } } + val max = (counts.maxOfOrNull { it.second } ?: 1).coerceAtLeast(1) + Column(Modifier.fillMaxSize().padding(20.dp)) { + Text("Sessions per track, day ${state.day}", style = MaterialTheme.typography.titleMedium) + Spacer(Modifier.height(16.dp)) + val bar = MaterialTheme.colorScheme.surfaceContainerHighest + Canvas(Modifier.fillMaxWidth().height(220.dp).testTag("chart")) { + val gap = 16.dp.toPx() + val w = (size.width - gap * (counts.size - 1)) / counts.size + counts.forEachIndexed { i, (track, n) -> + val x = i * (w + gap) + drawRoundRect(bar, Offset(x, 0f), Size(w, size.height), CornerRadius(8.dp.toPx())) + val h = size.height * n / max * grow + drawRoundRect(Color(track.color), Offset(x, size.height - h), Size(w, h), CornerRadius(8.dp.toPx())) + } + } + Spacer(Modifier.height(12.dp)) + counts.forEach { (track, n) -> + Row(Modifier.fillMaxWidth().padding(vertical = 6.dp), verticalAlignment = Alignment.CenterVertically) { + Box(Modifier.size(12.dp).background(Color(track.color), CircleShape)) + Spacer(Modifier.width(8.dp)) + Text(track.label, Modifier.weight(1f)) + Text("$n", fontWeight = FontWeight.SemiBold) + } + } + AnimatedVisibility(state.favorites.isNotEmpty()) { + Text("You have ${state.favorites.size} favorite sessions.", Modifier.padding(top = 12.dp), + color = MaterialTheme.colorScheme.primary) + } + } +} diff --git a/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/Data.kt b/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/Data.kt new file mode 100644 index 00000000000..c061c7bbf57 --- /dev/null +++ b/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/Data.kt @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package keynote + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn + +enum class Track(val label: String, val color: Long) { + Language("Language", 0xFF7F52FF), + Multiplatform("Multiplatform", 0xFF0095D5), + Server("Server", 0xFF2E7D32), + Tooling("Tooling", 0xFFE65100), + UI("UI", 0xFFC2185B) +} + +data class Speaker(val name: String, val company: String) + +data class Session( + val id: Int, + val title: String, + val speaker: Speaker, + val track: Track, + val day: Int, + val startMinutes: Int, + val lengthMinutes: Int, + val room: String, + val description: String +) { + val timeLabel: String get() = clock(startMinutes) + " - " + clock(startMinutes + lengthMinutes) +} + +fun clock(minutes: Int): String { + val h = minutes / 60 + val m = minutes % 60 + return (if (h < 10) "0" else "") + h + ":" + (if (m < 10) "0" else "") + m +} + +/// The conference programme. Generated from a fixed seed, so every build and +/// every run of the benchmark sees the same 150 sessions. +object Programme { + private val first = listOf("Ada", "Linus", "Grace", "Alan", "Barbara", "Ken", "Margaret", "Dennis", "Frances", "Edsger", + "Radia", "Bjarne", "Sophie", "Guido", "Hedy", "James") + private val last = listOf("Lovelace", "Hopper", "Turing", "Liskov", "Thompson", "Hamilton", "Ritchie", "Allen", + "Dijkstra", "Perlman", "Wilson", "Lamarr", "Gosling", "Kay", "Knuth", "Backus") + private val companies = listOf("JetBrains", "Google", "Square", "Netflix", "Touchlab", "Cash App", "Shopify", "Zalando") + private val topics = listOf("Coroutines", "Flows", "Compose", "K2", "Ktor", "Gradle", "Serialization", "Wasm", + "Native interop", "Context parameters", "Value classes", "Multiplatform libraries", "Testing", "Performance", + "Accessibility", "Animations", "State hoisting", "Server-side Kotlin", "Build speed", "Code sharing") + private val shapes = listOf("Deep dive into %s", "%s in production", "What's new in %s", "%s: lessons learned", + "Mastering %s", "%s from scratch", "The hidden cost of %s", "%s at scale") + private val rooms = listOf("Auditorium", "Room 1", "Room 2", "Workshop A", "Lightning stage") + + val sessions: List by lazy { generate() } + + private fun generate(): List { + var seed = 20261025L + fun next(n: Int): Int { + seed = (seed * 6364136223846793005L + 1442695040888963407L) + return ((seed ushr 33) % n).toInt() + } + val out = ArrayList() + var id = 0 + for (day in 1..3) { + var start = 9 * 60 + while (out.count { it.day == day } < 50) { + for (room in rooms) { + if (out.count { it.day == day } >= 50) break + val topic = topics[next(topics.size)] + val title = shapes[next(shapes.size)].replace("%s", topic) + val speaker = Speaker(first[next(first.size)] + " " + last[next(last.size)], companies[next(companies.size)]) + val len = if (room == "Lightning stage") 15 else if (room.startsWith("Workshop")) 90 else 45 + out.add(Session(id++, title, speaker, Track.entries[next(Track.entries.size)], day, start, len, room, + "$title. ${speaker.name} of ${speaker.company} walks through $topic: the model behind it, " + + "the mistakes teams make with it, and the patterns that hold up in real applications. " + + "Bring questions; the last ten minutes are open Q&A.")) + } + start += 60 + } + } + return out + } + + /// The programme as a feed would deliver it: off the main thread, after a + /// network-like delay. + fun load(): Flow> = flow { + delay(150) + emit(sessions) + }.flowOn(Dispatchers.Default) +} diff --git a/scripts/kmp-bench/run_bench.sh b/scripts/kmp-bench/run_bench.sh new file mode 100755 index 00000000000..2d21b149d00 --- /dev/null +++ b/scripts/kmp-bench/run_bench.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Codename One designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Codename One through http://www.codenameone.com/ if you +# need additional information or have any questions. +# +# Builds the Keynote app twice from the same Compose source -- once with +# Codename One (compose-compat, ParparVM) and once with Compose Multiplatform +# (Kotlin/Native + Skia) -- runs both on one iOS simulator and writes +# results.json and results.md into the work directory. +# +# run_bench.sh --work [--runs N] [--device ] [--maven-repo ] +# +# Start-up is process start to marker: each app prints "KMPBENCH first-frame +# " from the first main-thread turn after its UI is installed, and the +# start is the kernel's start time for the process, so simctl's own latency is +# in neither. Compose Multiplatform's marker runs before Skia has drawn, which +# favours it. Memory is physical footprint (footprint(1)) a few seconds after +# launch; resident size counts the shared dyld cache on the simulator. Run 1 of +# each is cold (first launch after install) and reported separately. +set -Eeuo pipefail +trap 'rc=$?; echo "run_bench.sh: FAILED at line $LINENO (exit $rc)" >&2' ERR + +WORK=""; RUNS=5; DEVICE=""; MAVEN_REPO_LOCAL="${MAVEN_REPO_LOCAL:-}" +while [ $# -gt 0 ]; do + case "$1" in + --work) WORK="$2"; shift 2 ;; + --runs) RUNS="$2"; shift 2 ;; + --device) DEVICE="$2"; shift 2 ;; + --maven-repo) MAVEN_REPO_LOCAL="$2"; shift 2 ;; + *) echo "unknown argument: $1" >&2; exit 2 ;; + esac +done +[ -n "$WORK" ] || { echo "usage: run_bench.sh --work [--runs N]" >&2; exit 2; } +HERE="$(cd "$(dirname "$0")" && pwd)" +mkdir -p "$WORK"; WORK="$(cd "$WORK" && pwd)" +MVN_REPO_ARG="" +[ -n "$MAVEN_REPO_LOCAL" ] && MVN_REPO_ARG="-Dmaven.repo.local=$MAVEN_REPO_LOCAL" +: "${JAVA17_HOME:?JAVA17_HOME must name a JDK 17}" + +"$HERE/app/prepare.sh" --work "$WORK" ${MAVEN_REPO_LOCAL:+--maven-repo "$MAVEN_REPO_LOCAL"} + +echo "== Codename One: translate and build" +( cd "$WORK/cn1" && JAVA_HOME="$JAVA17_HOME" mvn -B -q $MVN_REPO_ARG package -DskipTests \ + -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-source -Dopen=false ) > "$WORK/cn1-build.log" 2>&1 +SRC="$(dirname "$(find "$WORK/cn1/common/target" -maxdepth 2 -name Bench.xcodeproj | head -1)")" +( cd "$SRC" && xcodebuild -project Bench.xcodeproj -scheme Bench -configuration Release \ + -sdk iphonesimulator -arch arm64 -derivedDataPath "$WORK/dd" CODE_SIGNING_ALLOWED=NO build ) \ + > "$WORK/cn1-xcode.log" 2>&1 +CN1_APP="$WORK/dd/Build/Products/Release-iphonesimulator/Bench.app" +CN1_EXE="$CN1_APP/Bench" +CN1_ID=com.example.kmpbench + +echo "== Compose Multiplatform: build" +( cd "$WORK/cmp" && JAVA_HOME="$JAVA17_HOME" gradle --no-daemon -q \ + :composeApp:linkReleaseExecutableIosSimulatorArm64 ) > "$WORK/cmp-build.log" 2>&1 +# An executable, not a framework in an Xcode project: the bundle is the +# executable plus an Info.plist, which is all a simulator app needs. +CMP_APP="$WORK/Keynote.app"; CMP_EXE="$CMP_APP/Keynote"; CMP_ID=com.example.kmpbench.cmp +rm -rf "$CMP_APP"; mkdir -p "$CMP_APP" +cp "$WORK/cmp/composeApp/build/bin/iosSimulatorArm64/releaseExecutable/composeApp.kexe" "$CMP_EXE" +cp "$HERE/app/cmp/Info.plist" "$CMP_APP/Info.plist" +codesign -s - --force "$CMP_APP" > /dev/null 2>&1 + +stripped() { cp "$1" "$WORK/strip.tmp"; strip "$WORK/strip.tmp" 2>/dev/null || true; stat -f %z "$WORK/strip.tmp"; } +bundle() { du -sk "$1" | awk '{print $1 * 1024}'; } + +if [ -z "$DEVICE" ]; then + DEVICE="$(xcrun simctl list devices available | sed -n 's/.*iPhone 17 Pro (\([0-9A-F-]*\)).*/\1/p' | head -1)" +fi +[ -n "$DEVICE" ] || { echo "no iPhone simulator found; pass --device" >&2; exit 1; } +xcrun simctl boot "$DEVICE" 2>/dev/null || true +trap 'xcrun simctl shutdown "$DEVICE" 2>/dev/null || true' EXIT +xcrun simctl bootstatus "$DEVICE" > /dev/null + +# The kernel's start time of a process, epoch milliseconds. +started() { + python3 - "$1" <<'PY' +import ctypes, ctypes.util, struct, sys +libc = ctypes.CDLL(ctypes.util.find_library("c")) +mib = (ctypes.c_int * 4)(1, 14, 1, int(sys.argv[1])) # CTL_KERN, KERN_PROC, KERN_PROC_PID +buf = ctypes.create_string_buffer(1024); size = ctypes.c_size_t(1024) +libc.sysctl(mib, 4, buf, ctypes.byref(size), None, 0) +sec, usec = struct.unpack_from("qi", buf.raw, 0) # kp_proc.p_starttime +print(sec * 1000 + usec // 1000) +PY +} +# Prints one start-up time per run, milliseconds, space separated. +measure() { + local id="$1" name="$2" exe="$3" out="" + for i in $(seq 1 "$RUNS"); do + xcrun simctl terminate "$DEVICE" "$id" 2>/dev/null || true + local log="$WORK/$name-run$i.log" t0 t + ( timeout 20 xcrun simctl launch --console-pty "$DEVICE" "$id" > "$log" 2>&1 || true ) & + for _ in $(seq 1 100); do grep -q "KMPBENCH first-frame" "$log" 2>/dev/null && break; sleep 0.1; done + t="$(grep -o 'KMPBENCH first-frame [0-9]*' "$log" | awk '{print $3}' | head -1)" + [ -n "$t" ] || { echo "$name run $i printed no marker; see $log" >&2; exit 1; } + t0="$(started "$(pgrep -f "$exe" | head -1)")" + out="$out $((t - t0))" + sleep 3 + # While the app is still up: a launch is killed by its timeout after 20s. + [ "$i" = "$RUNS" ] && xcrun simctl io "$DEVICE" screenshot "$WORK/$name.png" > /dev/null 2>&1 || true + done + echo "$out" +} +footprint_bytes() { + xcrun simctl launch "$DEVICE" "$1" > /dev/null 2>&1; sleep 5 + footprint "$(pgrep -f "$2" | head -1)" 2>&1 \ + | awk '/Footprint:/ { for (i = 1; i <= NF; i++) if ($i == "Footprint:") { + v = $(i + 1); u = $(i + 2); print int(v * (u == "GB" ? 1073741824 : u == "MB" ? 1048576 : 1024)); exit } }' +} + +for side in cn1 cmp; do + if [ $side = cn1 ]; then app="$CN1_APP"; id=$CN1_ID; exe=Bench.app/Bench + else app="$CMP_APP"; id=$CMP_ID; exe=Keynote.app/Keynote; fi + xcrun simctl uninstall "$DEVICE" "$id" 2>/dev/null || true + xcrun simctl install "$DEVICE" "$app" + eval "STARTS_$side=\"\$(measure \"\$id\" \"\$side\" \"\$exe\")\"" +done +CN1_RSS="$(footprint_bytes $CN1_ID Bench.app/Bench)"; CMP_RSS="$(footprint_bytes $CMP_ID Keynote.app/Keynote)" + +python3 - "$WORK" < allClasses) { } if(isInterface) { - b.append("int **classToInterfaceMap_"); - b.append(clsName); - b.append(";\n"); + appendInterfaceMap(b, allClasses); } fullFieldList = new ArrayList(); @@ -1963,8 +1962,7 @@ public String generateCCode(List allClasses) { // we pretend to have a virtual method here but the optimizer says its not really needed if(!m.isVirtualOverriden()) { m.setThunkCases(thunkCasesFor(m)); - m.appendVirtualMethodC(clsName, b, "classToInterfaceMap_" + clsName + - "[cn1__cls->classId][" + offset + "]", true); + m.appendVirtualMethodC(clsName, b, interfaceSlot(offset), true); m.setThunkCases(null); } offset++; @@ -2034,13 +2032,13 @@ public String generateCCode(List allClasses) { // ACQUIRE, not a plain load. This is the fast path of a double-checked // initialisation: the completing store below is a RELEASE, and the two // together are what make the writes this function performed -- the - // vtable, and every classToInterfaceMap_[classId] row -- visible - // to a thread that observes the flag set. + // vtable -- visible to a thread that observes the flag set. // // With plain accesses on arm64 a second thread could see LOADED==1 while // those table stores were still invisible, then index a row that read as - // NULL. OBSERVED: three identical SIGSEGVs at - // classToInterfaceMap_java_util_NavigableMap[classId] + 0x8, reached from + // NULL. OBSERVED: three identical SIGSEGVs at what was then the run-time + // built classToInterfaceMap_java_util_NavigableMap[classId] + 0x8 (the map + // is constant data now, see appendInterfaceMap), reached from // TreeSet.clear -> the interface dispatch for NavigableMap.clear, in a // translator that is single-threaded in its own code but shares the // process with the GC thread, which also runs Java and so also runs @@ -2081,57 +2079,20 @@ public String generateCCode(List allClasses) { b.append(".vtable = initVtableForInterface();\n "); } - // create the vtable - b.append(" class__"); - b.append(clsName); - b.append(".vtable = malloc(sizeof(void*) *"); - b.append(virtualMethodList.size()); - b.append(");\n"); if(isInterface) { // special case, java_lang_object calls on interfaces should - // act like standard virtual method calls + // act like standard virtual method calls. The dispatch map is constant + // data (appendInterfaceMap), so there is nothing else to build here. b.append(" class__"); b.append(clsName); b.append(".vtable = initVtableForInterface();\n"); - b.append(" classToInterfaceMap_"); - b.append(clsName); - // calloc, not malloc: rows are filled only for classes that implement - // this interface, so an id that does not read as a registered row must - // read as NULL rather than as whatever the allocator last left there. - b.append(" = calloc(cn1_array_start_offset, sizeof(int*));\n"); - for(ByteCodeClass cls : allClasses) { - if(!cls.isInterface) { - if(cls.doesImplement(this)) { - b.append(" classToInterfaceMap_"); - b.append(clsName); - b.append("[cn1_class_id_"); - b.append(cls.clsName); - // sizeof(int), not sizeof(int*): a row is an int[] of vtable - // slot indices -- the map is int**, so the ROW is what is - // int-sized. Asking for pointer size allocated twice what the - // row can ever hold, on every interface/implementor pair. - b.append("] = malloc(sizeof(int) * "); - b.append(getMethodCountIncludingBase()); - b.append(");\n"); - offset = 0; - for(BytecodeMethod m : virtualMethodList) { - if(!m.getClsName().equals("java_lang_Object")) { - b.append(" classToInterfaceMap_"); - b.append(clsName); - b.append("[cn1_class_id_"); - b.append(cls.clsName); - b.append("]["); - b.append(offset); - b.append("] = "); - b.append(cls.virtualMethodList.indexOf(m)); - b.append(";\n"); - offset++; - } - } - } - } - } } else { + // create the vtable + b.append(" class__"); + b.append(clsName); + b.append(".vtable = malloc(sizeof(void*) *"); + b.append(virtualMethodList.size()); + b.append(");\n"); b.append(" __INIT_VTABLE_"); b.append(clsName); b.append("(threadStateData, class__"); @@ -2151,8 +2112,8 @@ public String generateCCode(List allClasses) { // that recursion guard. Nothing outside this function may treat it as // "safe to use the class" in the JLS sense -- a class under initialization // is not finished. The release is what the INLINE GUARDS acquire against: - // they test this flag, and it is what publishes the vtable and the - // classToInterfaceMap rows written just above. Guarding them on + // they test this flag, and it is what publishes the vtable written just + // above. Guarding them on // __X_LOADED__ instead would also be correct about the vtable and would // additionally hold other threads until __CLINIT__ returned -- a strictly // later gate than master opens, which moved layout on four native ports @@ -2313,6 +2274,131 @@ private static char onDeviceDebugTypeCharFor(ByteCodeField bf) { * @param interfaceObj the interface to test * @return true when this class is assignable to it */ + /// The interface dispatch map, emitted as constant data. + /// + /// Dispatching a method of an interface needs, for the receiver's class, the + /// vtable slot that implements it. That used to be a table built by the + /// interface's static initializer: one `calloc` of a pointer per class in the + /// program, plus one `malloc`ed row per implementing class. With 3,500 classes + /// that was 28 KB per interface whatever its implementers, and a Compose + /// application initializes ninety of them -- 2.5 MB of dirty heap, written once + /// and never changed, describing nothing the translator did not already know. + /// + /// It is now two `static const` arrays, which the linker puts in read-only + /// pages that are mapped from the binary and never dirtied: + /// + /// - `cn1_itrows_`: the rows, one slot per method that is not + /// Object's. Classes that inherit one layout share one row, so the + /// hundred-odd implementers of `Iterator` take a handful. + /// - `cn1_itix_`: for every class id between the lowest and highest + /// implementer, where its row starts. Only implementers ever index it -- + /// the thunk is only reached with a receiver of the interface's type -- so + /// ids outside that range need no entry and no bounds check. + /// + /// The lookup is the same two dependent loads as before, and needs no + /// publication: nothing is written at run time, so no thread can read it half + /// built. + private void appendInterfaceMap(StringBuilder b, List allClasses) { + List slots = new ArrayList(); + for(BytecodeMethod m : virtualMethodList) { + if(!m.getClsName().equals("java_lang_Object")) { + slots.add(m); + } + } + interfaceMapBase = 0; + if(slots.isEmpty()) { + return; + } + List implementers = new ArrayList(); + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for(ByteCodeClass cls : allClasses) { + // An abstract class is never the class of an object, so no dispatch reads its row. + if(!cls.isInterface && !cls.isAbstract && cls.doesImplement(this)) { + implementers.add(cls); + min = Math.min(min, cls.getClassOffset()); + max = Math.max(max, cls.getClassOffset()); + } + } + if(implementers.isEmpty()) { + // Never dispatched through: no object of this type can exist. The thunks + // still compile against an empty map. + b.append("static const unsigned short cn1_itrows_").append(clsName).append("[1] __attribute__((unused));\n"); + b.append("static inline JAVA_INT cn1_itrow_").append(clsName).append("(JAVA_INT id) __attribute__((unused));\n"); + b.append("static inline JAVA_INT cn1_itrow_").append(clsName).append("(JAVA_INT id) {\n"); + b.append(" __builtin_trap();\n return 0;\n}\n"); + return; + } + Map rowOffsets = new HashMap(); + StringBuilder rows = new StringBuilder(); + int[] starts = new int[implementers.size()]; + int rowCount = 0; + int maxValue = 0; + boolean missing = false; + for(int k = 0 ; k < implementers.size() ; k++) { + ByteCodeClass cls = implementers.get(k); + StringBuilder row = new StringBuilder(); + for(BytecodeMethod m : slots) { + int idx = cls.virtualMethodList.indexOf(m); + maxValue = Math.max(maxValue, idx); + missing |= idx < 0; + row.append(idx).append(','); + } + String key = row.toString(); + Integer at = rowOffsets.get(key); + if(at == null) { + at = rowCount * slots.size(); + rowOffsets.put(key, at); + rows.append("\n ").append(key); + rowCount++; + } + starts[k] = at; + } + int lastStart = (rowCount - 1) * slots.size() + 1; + // A concrete class without the method (a stale or mismatched class file) gets -1, as it + // did when the rows were written at start-up: a call through it fails as it always has, + // and a class that is never called through still links. An unsigned row would turn the + // -1 into 65535, an index past any vtable, so such a table is stored signed. + String rowType = missing ? (maxValue < 32768 ? "short" : "JAVA_INT") + : (maxValue < 65536 ? "unsigned short" : "JAVA_INT"); + String ixType = lastStart < 65536 ? "unsigned short" : "JAVA_INT"; + b.append("static const ").append(rowType).append(" cn1_itrows_").append(clsName) + .append("[] __attribute__((unused)) = {"); + b.append(rows).append("\n};\n"); + b.append("static const ").append(ixType).append(" cn1_itix_").append(clsName) + .append("[").append(max - min + 1).append("] __attribute__((unused)) = {"); + for(int k = 0 ; k < implementers.size() ; k++) { + b.append(k == 0 ? "\n [" : ",\n ["); + b.append("cn1_class_id_").append(implementers.get(k).clsName).append(" - ").append(min); + // Plus one: an id the designated initializers skip reads 0, "implements nothing". + b.append("] = ").append(starts[k] + 1); + } + b.append("\n};\n"); + interfaceMapBase = min; + appendRowLookup(b, String.valueOf(max - min + 1), "cn1_itix_" + clsName + "[d]"); + } + + /// The row of the receiver's class. A receiver that does not implement the interface -- its id + /// outside the table or in a gap of it -- traps. It can only get here through a cast that did + /// not hold, and ParparVM's CHECKCAST is unchecked; the old map read a NULL row and faulted + /// the same way, where reading the table out of bounds would jump through a random row. + private void appendRowLookup(StringBuilder b, String len, String read) { + b.append("static inline JAVA_INT cn1_itrow_").append(clsName).append("(JAVA_INT id) __attribute__((unused));\n"); + b.append("static inline JAVA_INT cn1_itrow_").append(clsName).append("(JAVA_INT id) {\n"); + b.append(" unsigned int d = (unsigned int)(id - ").append(interfaceMapBase).append(");\n"); + b.append(" JAVA_INT v = d < ").append(len).append("u ? ").append(read).append(" : 0;\n"); + b.append(" if (__builtin_expect(v == 0, 0)) __builtin_trap();\n"); + b.append(" return v - 1;\n}\n"); + } + + /// The lowest class id cn1_itix_ covers; see appendInterfaceMap. + private int interfaceMapBase; + + /// The vtable slot of the receiver's class for the interface method at `slot`. + private String interfaceSlot(int slot) { + return "cn1_itrows_" + clsName + "[cn1_itrow_" + clsName + "(cn1__cls->classId) + " + slot + "]"; + } + boolean doesImplement(ByteCodeClass interfaceObj) { if(baseInterfacesObject != null) { if(baseInterfacesObject.contains(interfaceObj)) { @@ -3160,7 +3246,7 @@ public boolean hasMarkWork() { /// from then on it is always false. Two kinds qualify: /// /// - A class with NO . Its __STATIC_INITIALIZER only mallocs and fills its - /// vtable, or for an interface its classToInterfaceMap rows. These run first, in + /// vtable, or for an interface points its vtable at Object's. These run first, in /// cn1EagerInitClasses, at the very start of initConstantPool. /// - A class whose is PURE (see computePureClinits): it reads and writes /// only its own static fields, constants, string literals and arrays it creates, diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeField.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeField.java index a37abe13c45..21764cac66f 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeField.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeField.java @@ -42,6 +42,7 @@ public class ByteCodeField { private Object value; private boolean privateField; private final boolean volatileField; + private final String descriptor; public ByteCodeField(String clsName, int access, String name, String desc, String signature, Object value) { this.clsName = clsName; @@ -54,6 +55,7 @@ public ByteCodeField(String clsName, int access, String name, String desc, Strin finalField = (access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL; volatileField = (access & Opcodes.ACC_VOLATILE) == Opcodes.ACC_VOLATILE; fieldName = name.replace('$', '_').replace('-', '_'); + descriptor = desc; arrayDimensions = 0; while(desc.startsWith("[")) { @@ -103,6 +105,11 @@ public ByteCodeField(String clsName, int access, String name, String desc, Strin } } + /// The JVM descriptor the field was declared with, e.g. `Ljava/lang/Object;`. + public String getDescriptor() { + return descriptor; + } + public String getFieldName() { return fieldName; } diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java index fd9dac1e388..b13d596eb53 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java @@ -1014,21 +1014,24 @@ private static void handleAppleOutput(ByteCodeTranslator b, File[] sources, File } } optionalFrameworks.add("UserNotifications.framework"); - includeFrameworks.add("libiconv.dylib"); + // Only what the port's natives call. A linked framework is loaded at launch whether or not + // anything calls it, with everything it links in turn, so one listed here "in case" costs + // every application memory and start-up time. MapKit, GameKit, QuickLook, AddressBook, + // libxml2, libz and libiconv were listed here and called by nothing, and loading them cost + // a Compose application 11 MB of dyld memory on the simulator, a fifth of its footprint. + // A feature that needs a framework adds it when it is used -- the maps provider brings + // MapKit (MapsProviderInjector), contacts bring AddressBook, App Attest brings + // DeviceCheck -- and a cn1lib declares its own through ios.add_libs. //includeFrameworks.add("AdSupport.framework"); // AddressBookUI and AddressBook are iOS-only. On macOS the contacts API - // is Contacts.framework, and linking the iOS ones fails outright rather - // than merely going unused. - if (platform.hasIosDeviceIdioms()) { - includeFrameworks.add("AddressBookUI.framework"); - includeFrameworks.add("AddressBook.framework"); - } else { + // is Contacts.framework, and linking the iOS ones fails outright. + // On iOS the builder links them with INCLUDE_CONTACTS_USAGE, the define their natives + // compile under (IPhoneBuilder, beside ios.add_libs). + if (!platform.hasIosDeviceIdioms()) { includeFrameworks.add("Contacts.framework"); } includeFrameworks.add("SystemConfiguration.framework"); - includeFrameworks.add("MapKit.framework"); includeFrameworks.add("AudioToolbox.framework"); - includeFrameworks.add("libxml2.dylib"); includeFrameworks.add("QuartzCore.framework"); if (!isBundledSqliteCipherEnabled()) { // The system libsqlite3 has no cipher support, so an application that encrypts @@ -1038,7 +1041,6 @@ private static void handleAppleOutput(ByteCodeTranslator b, File[] sources, File includeFrameworks.add("libsqlite3.dylib"); includeFrameworks.add("libsqlite3.0.dylib"); } - includeFrameworks.add("GameKit.framework"); includeFrameworks.add("Security.framework"); //includeFrameworks.add("StoreKit.framework"); // CoreMotion has no macOS implementation -- a Mac has no accelerometer to @@ -1071,10 +1073,8 @@ private static void handleAppleOutput(ByteCodeTranslator b, File[] sources, File includeFrameworks.add("AVFoundation.framework"); includeFrameworks.add("CoreText.framework"); includeFrameworks.add("CoreVideo.framework"); - includeFrameworks.add("QuickLook.framework"); //includeFrameworks.add("iAd.framework"); includeFrameworks.add("CoreMedia.framework"); - includeFrameworks.add("libz.dylib"); includeFrameworks.add("AVKit.framework"); if(!addFrameworks.equalsIgnoreCase("none")) { includeFrameworks.addAll(Arrays.asList(Util.splitLiteral(addFrameworks, ';'))); diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java index d475d443c7c..b910dcfd01a 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java @@ -2413,8 +2413,8 @@ public void appendVirtualMethodC(String cls, StringBuilder b, String offset, boo b.append("\n#if CN1_TAGGED_ACTIVE\n if(CN1_TAG_CODE(__cn1ThisObject) == CN1_TAG_INTEGER && CN1_TAG_CODE(__cn1Arg1) == CN1_TAG_INTEGER) { return (__cn1ThisObject == __cn1Arg1) ? JAVA_TRUE : JAVA_FALSE; }\n#endif\n "); } - // An eagerly initialized interface (no ) had its classToInterfaceMap - // rows written in initConstantPool before any Java thread existed, so there is + // An eagerly initialized interface (no ) had its vtable written in + // initConstantPool before any Java thread existed, so there is // nothing left for this guard to publish -- see // ByteCodeClass.isEagerInitEligible. if(includeStaticInitializer && !ByteCodeClass.eagerInit(cls)) { @@ -2432,9 +2432,9 @@ public void appendVirtualMethodC(String cls, StringBuilder b, String offset, boo // self time doing nothing but returning. // // The flag is the right thing to test and the ordering is not incidental: - // class__X.initialized is RELEASE-stored after the vtable and the - // classToInterfaceMap rows are written, and this thunk is about to index - // exactly those rows -- so the ACQUIRE load here is what makes them visible. + // class__X.initialized is RELEASE-stored after the interface's vtable is + // written, and this thunk may read it -- so the ACQUIRE load here is what + // makes it visible. (The dispatch map itself is constant data.) // Testing __X_LOADED__ instead would be a weaker gate that publishes // nothing; that distinction is why the inline guards were moved onto this // flag in the first place. @@ -2450,8 +2450,8 @@ public void appendVirtualMethodC(String cls, StringBuilder b, String offset, boo // CN1_CLASS_OF is not a field load when tagged values are compiled in: it // masks the pointer, tests the tag and selects between a proxy and the object // before it loads anything. The interface form of this thunk used it twice -- - // once for the vtable and once for the classId that indexes - // classToInterfaceMap -- so resolving it once here removes that work from + // once for the vtable and once for the classId that indexes the + // interface map -- so resolving it once here removes that work from // every interface dispatch, which is the hottest indirect call the VM makes. // Skip the tagged resolve where a tagged value cannot arrive: this thunk only // ever sees objects whose class is its owner or below, and a boxed immediate @@ -2467,7 +2467,7 @@ public void appendVirtualMethodC(String cls, StringBuilder b, String offset, boo /* A SWITCH ON THE CLASS ID, ahead of the indirect dispatch below. * * The indirect form is six dependent loads -- class pointer, class id, the - * classToInterfaceMap row, the offset within it, the vtable, the slot -- and + * interface map row, the offset within it, the vtable, the slot -- and * then a branch the C compiler cannot see through, so nothing on either side * of the call is optimised across it. The switch is ONE load and a jump table, * and every arm is a named callee that ThinLTO can inline. diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java index 07e522c1d4f..b49770ac756 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java @@ -492,6 +492,7 @@ public static void cleanup() { + " refused=" + BytecodeMethod.stackIterRefused); } LocalReceiverTypes.clear(); + RuntimeLowering.cleanup(); cn1SubclassIndex = null; cn1SubclassIndexSize = -1; nativeSources = null; @@ -560,19 +561,44 @@ public static void parse(File sourceFile) throws Exception { try (InputStream in = new FileInputStream(sourceFile)) { r = new ClassReader(in); } + if (RuntimeLowering.isReplacedStub(r.getClassName())) { + // Regenerated from the inputs' service files once everything is parsed. + return; + } Parser p = new Parser(); p.clsName = r.getClassName().replace('/', '_').replace('$', '_').replace('-', '_'); p.cls = new ByteCodeClass(p.clsName, r.getClassName()); readingClassDepth++; try { - r.accept(p, ClassReader.EXPAND_FRAMES); + r.accept(RuntimeLowering.adapt(p), ClassReader.EXPAND_FRAMES); } finally { readingClassDepth--; } classes.add(p.cls); } + + /// Writes a class the translator generates itself into a [ClassVisitor], as a + /// class file would be read into one. + interface SyntheticEmitter { + void emit(ClassVisitor cv); + } + + /// Parses a generated class exactly as [#parse(File)] parses one read from disk. + static void parseSynthetic(String internalName, SyntheticEmitter emitter) { + BytecodeMethod.setDependencyGraph(dependencyGraph); + Parser p = new Parser(); + p.clsName = internalName.replace('/', '_').replace('$', '_').replace('-', '_'); + p.cls = new ByteCodeClass(p.clsName, internalName); + readingClassDepth++; + try { + emitter.emit(p); + } finally { + readingClassDepth--; + } + classes.add(p.cls); + } private static ByteCodeClass getClassByName(String name) { return classIndex().get(name.replace('/', '_').replace('$', '_').replace('-', '_')); @@ -1390,6 +1416,9 @@ private static String fourChars(String s) { } public static void writeOutput(File outputDirectory) throws Exception { + // Field updaters and service providers: classes generated from what the parse + // found, which every later pass has to see as if they had been read. + RuntimeLowering.writeSynthetics(outputDirectory); // Must run before anything is emitted: the instanceof call sites bake the // dense bit index in, so the assignment has to exist before the first one. collectTypeTests(); diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/RuntimeLowering.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/RuntimeLowering.java new file mode 100644 index 00000000000..3a15cbd5f39 --- /dev/null +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/RuntimeLowering.java @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.tools.translator; + +import com.codename1.tools.translator.classfile.ClassVisitor; +import com.codename1.tools.translator.classfile.Label; +import com.codename1.tools.translator.classfile.MethodVisitor; +import com.codename1.tools.translator.classfile.Opcodes; +import com.codename1.tools.translator.classfile.Type; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/// The two JDK facilities a device cannot provide at run time, provided at build time. +/// +/// Both are reflection in disguise, and both are everywhere in Kotlin libraries: +/// kotlinx.coroutines, the Compose runtime, atomicfu and Ktor are built on them. +/// +/// #### Field updaters +/// +/// `AtomicReferenceFieldUpdater.newUpdater(Owner.class, V.class, "field")` (and the +/// `Integer` and `Long` forms) finds a field by name. Every compiler emits the +/// arguments as constants right before the call, so [#adapt] recognises the three (or +/// two) `ldc` instructions and the `invokestatic`, and replaces them with +/// `new Owner$cn1fu$field()`: a subclass of the updater that [#writeSynthetics] +/// generates for that one field. Its `get` and `set` are a plain `getfield` and +/// `putfield`, which on a volatile field are already atomic. Its `compareAndSet` +/// calls a static `cas0`, which is: +/// +/// - on a native target, a native whose C body is generated here -- a C11 compare and +/// exchange on the struct member, behind the same SATB and generational barriers +/// the generated setter applies. A lock would not do: code that uses an updater +/// also writes the field directly (atomicfu lowers `value = x` to a volatile +/// store), and only a hardware CAS is atomic with respect to that store; +/// - on JavaScript, branch-free bytecode: every thread there runs on one host thread, +/// so nothing can run between the read and the write. +/// +/// A call whose arguments are not constants is left alone, and reaches the JavaAPI +/// method, which throws. +/// +/// #### Services +/// +/// `java.util.ServiceLoader` cannot search a class path. [#recordResource] collects +/// every `META-INF/services` file among the translated inputs, and the generated +/// `java.util.ServiceLoader$Registry.providers(Class)` instantiates the providers each +/// names, in order. The JavaAPI stub of that class is skipped when parsing +/// ([#isReplacedStub]) and always regenerated, empty for an application that has no +/// service files. +final class RuntimeLowering { + static final String REGISTRY = "java/util/ServiceLoader$Registry"; + private static final String REF_UPDATER = "java/util/concurrent/atomic/AtomicReferenceFieldUpdater"; + private static final String INT_UPDATER = "java/util/concurrent/atomic/AtomicIntegerFieldUpdater"; + private static final String LONG_UPDATER = "java/util/concurrent/atomic/AtomicLongFieldUpdater"; + private static final String GENERATED_C = "cn1_runtime_lowering"; + private static final int CLASS_VERSION_8 = 52; + + /// Every field an updater was resolved for, keyed by the generated class name. + private static final Map UPDATERS = new TreeMap(); + /// Service interface (binary name) to provider class names, in file order. + private static final Map> SERVICES = new TreeMap>(); + + private RuntimeLowering() { + } + + static void cleanup() { + UPDATERS.clear(); + SERVICES.clear(); + } + + static boolean isReplacedStub(String internalName) { + return REGISTRY.equals(internalName); + } + + /// One resolved updater: the generated class, the class named as the owner, and + /// the field. + private static final class Updater { + final String generated; + final String owner; + final String field; + final String updaterClass; + /// `I`, `J` or `L` -- the updater's kind, not the field's declared type. + final char kind; + + Updater(String generated, String owner, String field, String updaterClass, char kind) { + this.generated = generated; + this.owner = owner; + this.field = field; + this.updaterClass = updaterClass; + this.kind = kind; + } + } + + /// Wraps the parser's visitor so updater call sites are rewritten as they are read. + static ClassVisitor adapt(ClassVisitor parser) { + return new ClassVisitor(parser) { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + return mv == null ? null : new CallSites(mv); + } + }; + } + + /// Holds back trailing `ldc` instructions until it knows whether a `newUpdater` + /// follows them. + private static final class CallSites extends MethodVisitor { + private final List pending = new ArrayList(); + + CallSites(MethodVisitor mv) { + super(mv); + } + + private void flush() { + for (Object o : pending) { + mv.visitLdcInsn(o); + } + pending.clear(); + } + + @Override + public void visitLdcInsn(Object value) { + pending.add(value); + if (pending.size() > 3) { + mv.visitLdcInsn(pending.remove(0)); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (opcode == Opcodes.INVOKESTATIC && "newUpdater".equals(name) && tryResolve(owner)) { + return; + } + flush(); + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } + + private boolean tryResolve(String updaterClass) { + char kind; + int args; + if (REF_UPDATER.equals(updaterClass)) { + kind = 'L'; + args = 3; + } else if (INT_UPDATER.equals(updaterClass)) { + kind = 'I'; + args = 2; + } else if (LONG_UPDATER.equals(updaterClass)) { + kind = 'J'; + args = 2; + } else { + return false; + } + if (pending.size() < args) { + return false; + } + int base = pending.size() - args; + Object ownerConst = pending.get(base); + Object nameConst = pending.get(pending.size() - 1); + if (!(ownerConst instanceof Type) || ((Type) ownerConst).getSort() != Type.OBJECT + || !(nameConst instanceof String) + || (args == 3 && !(pending.get(base + 1) instanceof Type))) { + return false; + } + String owner = ((Type) ownerConst).getInternalName(); + String field = (String) nameConst; + String generated = owner + "$cn1fu$" + field; + synchronized (UPDATERS) { + if (!UPDATERS.containsKey(generated)) { + UPDATERS.put(generated, new Updater(generated, owner, field, updaterClass, kind)); + } + } + for (int i = 0; i < base; i++) { + mv.visitLdcInsn(pending.get(i)); + } + pending.clear(); + mv.visitTypeInsn(Opcodes.NEW, generated); + mv.visitInsn(Opcodes.DUP); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, generated, "", "()V", false); + return true; + } + + @Override public void visitInsn(int opcode) { flush(); mv.visitInsn(opcode); } + @Override public void visitIntInsn(int opcode, int operand) { flush(); mv.visitIntInsn(opcode, operand); } + @Override public void visitVarInsn(int opcode, int var) { flush(); mv.visitVarInsn(opcode, var); } + @Override public void visitTypeInsn(int opcode, String type) { flush(); mv.visitTypeInsn(opcode, type); } + @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { + flush(); + mv.visitFieldInsn(opcode, owner, name, desc); + } + @Override public void visitInvokeDynamicInsn(String name, String desc, + com.codename1.tools.translator.classfile.Handle bsm, Object... bsmArgs) { + flush(); + mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + } + @Override public void visitJumpInsn(int opcode, Label label) { flush(); mv.visitJumpInsn(opcode, label); } + @Override public void visitLabel(Label label) { flush(); mv.visitLabel(label); } + @Override public void visitIincInsn(int var, int increment) { flush(); mv.visitIincInsn(var, increment); } + @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { + flush(); + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + flush(); + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + @Override public void visitMultiANewArrayInsn(String desc, int dims) { flush(); mv.visitMultiANewArrayInsn(desc, dims); } + @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { + flush(); + mv.visitTryCatchBlock(start, end, handler, type); + } + @Override public void visitLineNumber(int line, Label start) { flush(); mv.visitLineNumber(line, start); } + @Override public void visitMaxs(int maxStack, int maxLocals) { flush(); mv.visitMaxs(maxStack, maxLocals); } + @Override public void visitEnd() { flush(); mv.visitEnd(); } + } + + /// Records a `META-INF/services` file met among the inputs. Returns true when `f` is + /// one. + static boolean recordResource(File f) throws IOException { + File parent = f.getParentFile(); + if (parent == null || !"services".equals(parent.getName()) || parent.getParentFile() == null + || !"META-INF".equals(parent.getParentFile().getName())) { + return false; + } + String text; + try (InputStream in = new FileInputStream(f)) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[4096]; + int n; + while ((n = in.read(buf)) > 0) { + out.write(buf, 0, n); + } + text = new String(out.toByteArray(), StandardCharsets.UTF_8); + } + Set providers; + synchronized (SERVICES) { + providers = SERVICES.get(f.getName()); + if (providers == null) { + providers = new LinkedHashSet(); + SERVICES.put(f.getName(), providers); + } + } + int start = 0; + while (start <= text.length()) { + int end = text.indexOf('\n', start); + if (end < 0) { + end = text.length(); + } + String line = text.substring(start, end); + start = end + 1; + int hash = line.indexOf('#'); + String name = (hash >= 0 ? line.substring(0, hash) : line).trim(); + if (!name.isEmpty()) { + providers.add(name); + } + } + return true; + } + + /// Parses the generated classes and writes the C bodies of their natives. Runs once + /// every input is parsed and before anything is emitted. + static void writeSynthetics(File outputDirectory) throws IOException { + boolean javascript = ByteCodeTranslator.output == ByteCodeTranslator.OutputType.OUTPUT_TYPE_JAVASCRIPT; + StringBuilder c = new StringBuilder(); + Set headers = new LinkedHashSet(); + for (Updater u : UPDATERS.values()) { + ByteCodeField field = findField(u.owner, u.field); + if (field == null || field.isStaticField()) { + System.out.println("WARNING: " + u.updaterClass.substring(u.updaterClass.lastIndexOf('/') + 1) + + " names " + u.owner.replace('/', '.') + "." + u.field + + ", which is not an instance field of a translated class"); + continue; + } + String desc = field.getDescriptor(); + emitUpdaterClass(u, desc, !javascript); + if (!javascript) { + headers.add(Util.mangle(u.owner)); + headers.add(Util.mangle(u.generated)); + appendCasBody(c, u, field); + } + } + emitRegistry(); + File out = new File(outputDirectory, GENERATED_C + "." + ByteCodeTranslator.output.extension()); + if (javascript || c.length() == 0) { + if (out.exists() && !out.delete()) { + throw new IOException("Could not remove " + out); + } + return; + } + StringBuilder file = new StringBuilder(); + file.append("// Generated by the translator: compare-and-set for the field updaters.\n"); + file.append("#include \"cn1_globals.h\"\n#include \n"); + for (String h : headers) { + file.append("#include \"").append(h).append(".h\"\n"); + } + file.append(c); + try (OutputStream o = new FileOutputStream(out)) { + o.write(file.toString().getBytes(StandardCharsets.UTF_8)); + } + } + + private static ByteCodeField findField(String owner, String name) { + // ByteCodeField keeps the mangled name ($ and - folded to _). + String mangled = name.replace('$', '_').replace('-', '_'); + ByteCodeClass bc = Parser.getClassObject(Util.mangle(owner)); + while (bc != null) { + for (ByteCodeField f : bc.getFields()) { + if (f.getFieldName().equals(mangled)) { + return f; + } + } + String base = bc.getBaseClass(); + bc = base == null ? null : Parser.getClassObject(Util.mangle(base)); + } + return null; + } + + private static String valueDesc(Updater u) { + return u.kind == 'L' ? "Ljava/lang/Object;" : String.valueOf(u.kind); + } + + private static void emitUpdaterClass(final Updater u, final String fieldDesc, final boolean nativeCas) { + Parser.parseSynthetic(u.generated, new Parser.SyntheticEmitter() { + @Override + public void emit(ClassVisitor cv) { + String v = valueDesc(u); + cv.visit(CLASS_VERSION_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC, + u.generated, null, u.updaterClass, new String[0]); + + MethodVisitor m = cv.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); + m.visitCode(); + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitMethodInsn(Opcodes.INVOKESPECIAL, u.updaterClass, "", "()V", false); + m.visitInsn(Opcodes.RETURN); + m.visitMaxs(1, 1); + m.visitEnd(); + + m = cv.visitMethod(Opcodes.ACC_PUBLIC, "get", "(Ljava/lang/Object;)" + v, null, null); + m.visitCode(); + m.visitVarInsn(Opcodes.ALOAD, 1); + m.visitTypeInsn(Opcodes.CHECKCAST, u.owner); + m.visitFieldInsn(Opcodes.GETFIELD, u.owner, u.field, fieldDesc); + m.visitInsn(returnOp(u.kind)); + m.visitMaxs(2, 2); + m.visitEnd(); + + m = cv.visitMethod(Opcodes.ACC_PUBLIC, "set", "(Ljava/lang/Object;" + v + ")V", null, null); + m.visitCode(); + m.visitVarInsn(Opcodes.ALOAD, 1); + m.visitTypeInsn(Opcodes.CHECKCAST, u.owner); + m.visitVarInsn(loadOp(u.kind), 2); + if (u.kind == 'L') { + m.visitTypeInsn(Opcodes.CHECKCAST, Type.getType(fieldDesc).getInternalName()); + } + m.visitFieldInsn(Opcodes.PUTFIELD, u.owner, u.field, fieldDesc); + m.visitInsn(Opcodes.RETURN); + m.visitMaxs(4, 4); + m.visitEnd(); + + // compareAndSet(obj, expect, update): null-check, then cas0. + int wide = u.kind == 'J' ? 2 : 1; + String casDesc = "(Ljava/lang/Object;" + v + v + ")Z"; + m = cv.visitMethod(Opcodes.ACC_PUBLIC, "compareAndSet", casDesc, null, null); + m.visitCode(); + m.visitVarInsn(Opcodes.ALOAD, 1); + m.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Objects", "requireNonNull", + "(Ljava/lang/Object;)Ljava/lang/Object;", false); + m.visitVarInsn(loadOp(u.kind), 2); + m.visitVarInsn(loadOp(u.kind), 2 + wide); + m.visitMethodInsn(Opcodes.INVOKESTATIC, u.generated, "cas0", casDesc, false); + m.visitInsn(Opcodes.IRETURN); + m.visitMaxs(6, 2 + 2 * wide); + m.visitEnd(); + + if (nativeCas) { + m = cv.visitMethod(Opcodes.ACC_STATIC | Opcodes.ACC_NATIVE | Opcodes.ACC_PRIVATE, "cas0", casDesc, null, null); + m.visitEnd(); + } else { + // One host thread: read, compare, and write back either the update or + // what was there, with nothing able to run in between. + int same = 1 + 2 * wide; + String selDesc = "(Z" + v + v + ")" + v; + m = cv.visitMethod(Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE, "cas0", casDesc, null, null); + m.visitCode(); + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitTypeInsn(Opcodes.CHECKCAST, u.owner); + m.visitFieldInsn(Opcodes.GETFIELD, u.owner, u.field, fieldDesc); + m.visitVarInsn(loadOp(u.kind), 1); + m.visitMethodInsn(Opcodes.INVOKESTATIC, u.updaterClass, "cn1Same", "(" + v + v + ")Z", false); + m.visitVarInsn(Opcodes.ISTORE, same); + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitTypeInsn(Opcodes.CHECKCAST, u.owner); + m.visitVarInsn(Opcodes.ILOAD, same); + m.visitVarInsn(loadOp(u.kind), 1 + wide); + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitTypeInsn(Opcodes.CHECKCAST, u.owner); + m.visitFieldInsn(Opcodes.GETFIELD, u.owner, u.field, fieldDesc); + m.visitMethodInsn(Opcodes.INVOKESTATIC, u.updaterClass, "cn1Select", selDesc, false); + if (u.kind == 'L') { + m.visitTypeInsn(Opcodes.CHECKCAST, Type.getType(fieldDesc).getInternalName()); + } + m.visitFieldInsn(Opcodes.PUTFIELD, u.owner, u.field, fieldDesc); + m.visitVarInsn(Opcodes.ILOAD, same); + m.visitInsn(Opcodes.IRETURN); + m.visitMaxs(8, same + 1); + m.visitEnd(); + } + cv.visitEnd(); + } + }); + } + + private static int loadOp(char kind) { + return kind == 'L' ? Opcodes.ALOAD : kind == 'J' ? Opcodes.LLOAD : Opcodes.ILOAD; + } + + private static int returnOp(char kind) { + return kind == 'L' ? Opcodes.ARETURN : kind == 'J' ? Opcodes.LRETURN : Opcodes.IRETURN; + } + + /// The C body of one updater's `cas0`. + private static void appendCasBody(StringBuilder c, Updater u, ByteCodeField field) { + ByteCodeClass gen = Parser.getClassObject(Util.mangle(u.generated)); + BytecodeMethod cas = null; + for (BytecodeMethod m : gen.getMethods()) { + if ("cas0".equals(m.getMethodName())) { + cas = m; + } + } + StringBuilder header = new StringBuilder(); + cas.appendMethodHeader(header, Util.mangle(u.generated)); + String proto = header.toString().trim(); + proto = proto.substring(0, proto.length() - 1); + String ctype = u.kind == 'L' ? "JAVA_OBJECT" : u.kind == 'J' ? "JAVA_LONG" : "JAVA_INT"; + String member = "((struct obj__" + Util.mangle(u.owner) + "*)__cn1Arg1)->" + + field.getClsName() + "_" + field.getFieldName(); + c.append('\n').append(proto).append(" {\n"); + c.append(" ").append(ctype).append(" __cn1Expect = __cn1Arg2;\n"); + if (u.kind == 'L') { + c.append(" CN1_WRITE_BARRIER(__cn1Arg1, __cn1Arg3);\n"); + c.append(" CN1_SATB_DELETE(&").append(member).append(");\n"); + } + if (field.isVolatile()) { + c.append(" return atomic_compare_exchange_strong(&").append(member) + .append(", &__cn1Expect, __cn1Arg3) ? JAVA_TRUE : JAVA_FALSE;\n"); + } else { + c.append(" return __atomic_compare_exchange_n(&").append(member) + .append(", &__cn1Expect, __cn1Arg3, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? JAVA_TRUE : JAVA_FALSE;\n"); + } + c.append("}\n"); + } + + /// `ServiceLoader$Registry.providers(Class)`: compares the class against each + /// service and returns a fresh array of its providers. + private static void emitRegistry() { + final Map> table = new LinkedHashMap>(); + for (Map.Entry> e : SERVICES.entrySet()) { + String service = e.getKey().replace('.', '/'); + if (Parser.getClassObject(Util.mangle(service)) == null) { + continue; + } + List impls = new ArrayList(); + for (String p : e.getValue()) { + String internal = p.replace('.', '/'); + ByteCodeClass bc = Parser.getClassObject(Util.mangle(internal)); + if (bc == null) { + // A provider whose class was not translated: the JDK would fail to + // load it too, and the loader skips it. + continue; + } + impls.add(internal); + } + if (!impls.isEmpty()) { + table.put(service, impls); + } + } + Parser.parseSynthetic(REGISTRY, new Parser.SyntheticEmitter() { + @Override + public void emit(ClassVisitor cv) { + cv.visit(CLASS_VERSION_8, Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC, REGISTRY, null, + "java/lang/Object", new String[0]); + MethodVisitor m = cv.visitMethod(Opcodes.ACC_PRIVATE, "", "()V", null, null); + m.visitCode(); + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false); + m.visitInsn(Opcodes.RETURN); + m.visitMaxs(1, 1); + m.visitEnd(); + + m = cv.visitMethod(Opcodes.ACC_STATIC, "providers", "(Ljava/lang/Class;)[Ljava/lang/Object;", null, null); + m.visitCode(); + for (Map.Entry> e : table.entrySet()) { + Label next = new Label(); + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitLdcInsn(Type.getObjectType(e.getKey())); + m.visitJumpInsn(Opcodes.IF_ACMPNE, next); + List impls = e.getValue(); + pushInt(m, impls.size()); + m.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + for (int i = 0; i < impls.size(); i++) { + m.visitInsn(Opcodes.DUP); + pushInt(m, i); + m.visitTypeInsn(Opcodes.NEW, impls.get(i)); + m.visitInsn(Opcodes.DUP); + m.visitMethodInsn(Opcodes.INVOKESPECIAL, impls.get(i), "", "()V", false); + m.visitInsn(Opcodes.AASTORE); + } + m.visitInsn(Opcodes.ARETURN); + m.visitLabel(next); + } + m.visitInsn(Opcodes.ICONST_0); + m.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + m.visitInsn(Opcodes.ARETURN); + m.visitMaxs(6, 1); + m.visitEnd(); + cv.visitEnd(); + } + }); + } + + private static void pushInt(MethodVisitor m, int v) { + if (v <= 5) { + m.visitInsn(Opcodes.ICONST_0 + v); + } else { + m.visitIntInsn(Opcodes.SIPUSH, v); + } + } +} diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/bytecodes/Invoke.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/bytecodes/Invoke.java index 7634cd71a55..90fbba4839f 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/bytecodes/Invoke.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/bytecodes/Invoke.java @@ -492,7 +492,7 @@ public void appendInstruction(StringBuilder b) { // CLOSED-WORLD DEVIRT FOR INTERFACES. This was not attempted at all -- // the block above is gated on INVOKEVIRTUAL -- and interface dispatch is // the most expensive call this VM makes: a class id load, a - // classToInterfaceMap row, an offset, a vtable slot, then an indirect + // interface map row (cn1_itix_), an offset, a vtable slot, then an indirect // branch, none of which the C compiler can see through. // // A closed world does not have to guess. If exactly one reachable class diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/bytecodes/TypeInstruction.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/bytecodes/TypeInstruction.java index 2f6a69ba945..08334f84964 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/bytecodes/TypeInstruction.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/bytecodes/TypeInstruction.java @@ -303,8 +303,8 @@ public void appendInstruction(StringBuilder b, List l) { // set, so it never takes the class monitor and cannot rely on // the monitor's release. Pairs with the __ATOMIC_RELEASE store // in ByteCodeClass. A plain load here let a thread see the flag - // set while the vtable / classToInterfaceMap rows it describes - // were still invisible. + // set while the vtable it describes was still invisible. (Interface + // dispatch maps were once built here too; they are constant data now.) if(stackBuilderBytes > 0) { b.append("cn1StackBufferReset(&__cn1sbscope_").append(stackAllocId).append("); "); } diff --git a/vm/JavaAPI/src/java/lang/Class.java b/vm/JavaAPI/src/java/lang/Class.java index b679094dae9..1a352e2d5e9 100644 --- a/vm/JavaAPI/src/java/lang/Class.java +++ b/vm/JavaAPI/src/java/lang/Class.java @@ -559,6 +559,56 @@ public java.lang.reflect.Type[] getGenericInterfaces() { public native boolean isPrimitive(); + /// Reflective lookups find nothing on this platform: the translator keeps no member + /// tables, and strips what nothing calls. + public Method getDeclaredMethod(String name, Class... parameterTypes) throws NoSuchMethodException { + throw new NoSuchMethodException(name); + } + + public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException { + throw new NoSuchMethodException(name); + } + + public Constructor[] getConstructors() { + return new Constructor[0]; + } + + public Constructor[] getDeclaredConstructors() { + return new Constructor[0]; + } + + public Constructor getDeclaredConstructor(Class... parameterTypes) throws NoSuchMethodException { + throw new NoSuchMethodException(""); + } + + public Constructor getConstructor(Class... parameterTypes) throws NoSuchMethodException { + throw new NoSuchMethodException(""); + } + + public Method[] getMethods() { + return new Method[0]; + } + + public Method[] getDeclaredMethods() { + return new Method[0]; + } + + public java.lang.reflect.Field[] getFields() { + return new java.lang.reflect.Field[0]; + } + + public java.lang.reflect.Field[] getDeclaredFields() { + return new java.lang.reflect.Field[0]; + } + + public java.lang.reflect.Field getDeclaredField(String name) throws NoSuchFieldException { + throw new NoSuchFieldException(name); + } + + public java.lang.reflect.Field getField(String name) throws NoSuchFieldException { + throw new NoSuchFieldException(name); + } + public Method getEnclosingMethod() { return null; } diff --git a/vm/JavaAPI/src/java/lang/ClassLoader.java b/vm/JavaAPI/src/java/lang/ClassLoader.java index 8c5dd8b4520..e132f8d25e8 100644 --- a/vm/JavaAPI/src/java/lang/ClassLoader.java +++ b/vm/JavaAPI/src/java/lang/ClassLoader.java @@ -49,4 +49,19 @@ public static InputStream getSystemResourceAsStream(String name) { throw new UnsupportedOperationException(); } + /// Always empty: nothing on a device can be listed by name the way a class path's + /// entries can. Code that discovers services this way (kotlinx.coroutines' + /// fast loader) then falls back to [java.util.ServiceLoader], which the translator + /// fills from the `META-INF/services` files it was given. + public Class loadClass(String name) throws ClassNotFoundException { + throw new ClassNotFoundException(name); + } + + /// There is no class path to search. Fails rather than answering an empty + /// enumeration, so that a service lookup scanning `META-INF/services` falls back to + /// [java.util.ServiceLoader], which the translator fills in at build time. + public java.util.Enumeration getResources(String name) throws java.io.IOException { + throw new java.io.IOException("no class path on this platform: " + name); + } + } \ No newline at end of file diff --git a/vm/JavaAPI/src/java/lang/ClassValue.java b/vm/JavaAPI/src/java/lang/ClassValue.java new file mode 100644 index 00000000000..2debf0a6ba6 --- /dev/null +++ b/vm/JavaAPI/src/java/lang/ClassValue.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang; + +import java.util.HashMap; + +/// Lazily computes one value per class and keeps it until [#remove(Class)]. +public abstract class ClassValue { + private final HashMap, Object> values = new HashMap, Object>(); + private static final Object NULL = new Object(); + + protected ClassValue() { + } + + protected abstract T computeValue(Class type); + + @SuppressWarnings("unchecked") + public T get(Class type) { + Object v; + synchronized (values) { + v = values.get(type); + } + if (v == null) { + T computed = computeValue(type); + synchronized (values) { + Object raced = values.get(type); + if (raced != null) { + v = raced; + } else { + v = computed == null ? NULL : computed; + values.put(type, v); + } + } + } + return v == NULL ? null : (T) v; + } + + public void remove(Class type) { + synchronized (values) { + values.remove(type); + } + } +} diff --git a/vm/JavaAPI/src/java/lang/NoSuchFieldException.java b/vm/JavaAPI/src/java/lang/NoSuchFieldException.java new file mode 100644 index 00000000000..cf1085ae615 --- /dev/null +++ b/vm/JavaAPI/src/java/lang/NoSuchFieldException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang; + +/// Thrown by the reflective lookups, which find nothing on this platform. +public class NoSuchFieldException extends Exception { + public NoSuchFieldException() { + } + + public NoSuchFieldException(String message) { + super(message); + } +} diff --git a/vm/JavaAPI/src/java/lang/NoSuchMethodException.java b/vm/JavaAPI/src/java/lang/NoSuchMethodException.java new file mode 100644 index 00000000000..88442db9a3c --- /dev/null +++ b/vm/JavaAPI/src/java/lang/NoSuchMethodException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang; + +/// Thrown by the reflective lookups, which find nothing on this platform. +public class NoSuchMethodException extends Exception { + public NoSuchMethodException() { + } + + public NoSuchMethodException(String message) { + super(message); + } +} diff --git a/vm/JavaAPI/src/java/lang/Thread.java b/vm/JavaAPI/src/java/lang/Thread.java index a3c1b0d302f..181660cf5d2 100644 --- a/vm/JavaAPI/src/java/lang/Thread.java +++ b/vm/JavaAPI/src/java/lang/Thread.java @@ -66,6 +66,8 @@ public class Thread implements java.lang.Runnable{ private Runnable target; private boolean alive; + private boolean terminated; + private ClassLoader contextClassLoader; private String name; private int priority = NORM_PRIORITY; private long nativeThreadId; @@ -249,6 +251,28 @@ public boolean isInterrupted() { private native boolean isInterrupted(boolean clearInterrupted); + /// A thread's state, as far as this runtime tracks it: a thread that is alive is + /// reported RUNNABLE whether or not it is blocked. + public enum State { + NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED + } + + /// Kept for code that installs one; nothing here loads classes through it. + public ClassLoader getContextClassLoader() { + return contextClassLoader; + } + + public void setContextClassLoader(ClassLoader cl) { + contextClassLoader = cl; + } + + public State getState() { + if (alive) { + return State.RUNNABLE; + } + return terminated ? State.TERMINATED : State.NEW; + } + /** * Tests if this thread is alive. A thread is alive if it has been started and has not yet died. */ @@ -332,6 +356,7 @@ private void runImpl(long tid) { threadLocalValues = null; synchronized(this) { alive = false; + terminated = true; notifyAll(); } } diff --git a/vm/JavaAPI/src/java/lang/ref/ReferenceQueue.java b/vm/JavaAPI/src/java/lang/ref/ReferenceQueue.java new file mode 100644 index 00000000000..3bbcb22f3ea --- /dev/null +++ b/vm/JavaAPI/src/java/lang/ref/ReferenceQueue.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang.ref; + +/// A queue the collector never enqueues into: this collector clears references but +/// does not post them. [#poll()] therefore always answers null and [#remove()] blocks +/// until the thread is interrupted, which is what a cleaning loop waiting on it expects +/// of a queue that stays empty. +public class ReferenceQueue { + public ReferenceQueue() { + } + + public Reference poll() { + return null; + } + + public Reference remove() throws InterruptedException { + synchronized (this) { + while (true) { + wait(); + } + } + } + + public Reference remove(long timeout) throws InterruptedException { + if (timeout < 0) { + throw new IllegalArgumentException("negative timeout"); + } + synchronized (this) { + if (timeout == 0) { + while (true) { + wait(); + } + } + wait(timeout); + } + return null; + } +} diff --git a/vm/JavaAPI/src/java/lang/ref/WeakReference.java b/vm/JavaAPI/src/java/lang/ref/WeakReference.java index a1af9be37e1..10c425b4e07 100644 --- a/vm/JavaAPI/src/java/lang/ref/WeakReference.java +++ b/vm/JavaAPI/src/java/lang/ref/WeakReference.java @@ -58,4 +58,12 @@ public class WeakReference extends java.lang.ref.Reference{ public WeakReference(java.lang.Object ref){ super(ref, STRENGTH_WEAK); } + + /** + * Creates a weak reference registered with a queue. The collector never posts to + * a {@link ReferenceQueue} here, so the queue is accepted and not kept. + */ + public WeakReference(java.lang.Object ref, ReferenceQueue q){ + super(ref, STRENGTH_WEAK); + } } diff --git a/vm/JavaAPI/src/java/lang/reflect/Constructor.java b/vm/JavaAPI/src/java/lang/reflect/Constructor.java index d7968c78c60..ea490f5aaf9 100644 --- a/vm/JavaAPI/src/java/lang/reflect/Constructor.java +++ b/vm/JavaAPI/src/java/lang/reflect/Constructor.java @@ -23,6 +23,19 @@ package java.lang.reflect; public class Constructor { + public Class[] getParameterTypes() { + return new Class[0]; + } + + public int getModifiers() { + return 0; + } + + public Object newInstance(Object... args) throws InstantiationException, IllegalAccessException, + InvocationTargetException { + throw new InstantiationException("reflection is not supported on this platform"); + } + public String getName() { return null; } diff --git a/vm/JavaAPI/src/java/lang/reflect/Field.java b/vm/JavaAPI/src/java/lang/reflect/Field.java new file mode 100644 index 00000000000..57b69cf01ce --- /dev/null +++ b/vm/JavaAPI/src/java/lang/reflect/Field.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang.reflect; + +/// Declared so that code which reflects on a field as a fallback links. No lookup ever +/// answers one: [Class#getDeclaredField(String)] throws `NoSuchFieldException`. +public class Field { + Field() { + } + + public String getName() { + return null; + } + + public Class getType() { + return null; + } + + public int getModifiers() { + return 0; + } + + public void setAccessible(boolean flag) { + } + + public Object get(Object obj) throws IllegalAccessException { + throw new IllegalAccessException("reflection is not supported on this platform"); + } + + public void set(Object obj, Object value) throws IllegalAccessException { + throw new IllegalAccessException("reflection is not supported on this platform"); + } +} diff --git a/vm/JavaAPI/src/java/lang/reflect/GenericDeclaration.java b/vm/JavaAPI/src/java/lang/reflect/GenericDeclaration.java new file mode 100644 index 00000000000..26a2ec7c7b6 --- /dev/null +++ b/vm/JavaAPI/src/java/lang/reflect/GenericDeclaration.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang.reflect; + +/// Something that declares type variables. Nothing on this platform reports any. +public interface GenericDeclaration { + TypeVariable[] getTypeParameters(); +} diff --git a/vm/JavaAPI/src/java/lang/reflect/Method.java b/vm/JavaAPI/src/java/lang/reflect/Method.java index 3d292b37f21..adb6d89a510 100644 --- a/vm/JavaAPI/src/java/lang/reflect/Method.java +++ b/vm/JavaAPI/src/java/lang/reflect/Method.java @@ -23,6 +23,25 @@ package java.lang.reflect; public class Method { + public Object invoke(Object obj, Object... args) throws IllegalAccessException, InvocationTargetException { + throw new IllegalAccessException("reflection is not supported on this platform"); + } + + public void setAccessible(boolean flag) { + } + + public Class[] getParameterTypes() { + return new Class[0]; + } + + public Class getReturnType() { + return null; + } + + public int getModifiers() { + return 0; + } + public String getName() { return null; } diff --git a/vm/JavaAPI/src/java/lang/reflect/Modifier.java b/vm/JavaAPI/src/java/lang/reflect/Modifier.java new file mode 100644 index 00000000000..713472cc605 --- /dev/null +++ b/vm/JavaAPI/src/java/lang/reflect/Modifier.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang.reflect; + +/// The JVM access flags, for code that decodes a member's modifiers. +public class Modifier { + public static final int PUBLIC = 0x0001; + public static final int PRIVATE = 0x0002; + public static final int PROTECTED = 0x0004; + public static final int STATIC = 0x0008; + public static final int FINAL = 0x0010; + public static final int SYNCHRONIZED = 0x0020; + public static final int VOLATILE = 0x0040; + public static final int TRANSIENT = 0x0080; + public static final int NATIVE = 0x0100; + public static final int INTERFACE = 0x0200; + public static final int ABSTRACT = 0x0400; + public static final int STRICT = 0x0800; + + public Modifier() { + } + + public static boolean isPublic(int mod) { return (mod & PUBLIC) != 0; } + public static boolean isPrivate(int mod) { return (mod & PRIVATE) != 0; } + public static boolean isProtected(int mod) { return (mod & PROTECTED) != 0; } + public static boolean isStatic(int mod) { return (mod & STATIC) != 0; } + public static boolean isFinal(int mod) { return (mod & FINAL) != 0; } + public static boolean isSynchronized(int mod) { return (mod & SYNCHRONIZED) != 0; } + public static boolean isVolatile(int mod) { return (mod & VOLATILE) != 0; } + public static boolean isTransient(int mod) { return (mod & TRANSIENT) != 0; } + public static boolean isNative(int mod) { return (mod & NATIVE) != 0; } + public static boolean isInterface(int mod) { return (mod & INTERFACE) != 0; } + public static boolean isAbstract(int mod) { return (mod & ABSTRACT) != 0; } + public static boolean isStrict(int mod) { return (mod & STRICT) != 0; } +} diff --git a/vm/JavaAPI/src/java/net/URLStreamHandler.java b/vm/JavaAPI/src/java/net/URLStreamHandler.java new file mode 100644 index 00000000000..c6c642ed2e3 --- /dev/null +++ b/vm/JavaAPI/src/java/net/URLStreamHandler.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.net; + +/// Declared so that the [URL] constructor taking a handler links. No handler is +/// ever installed on this platform. +public abstract class URLStreamHandler { + public URLStreamHandler() { + } +} diff --git a/vm/JavaAPI/src/java/util/HashMap.java b/vm/JavaAPI/src/java/util/HashMap.java index 45235d0c6b6..4744bd653a4 100644 --- a/vm/JavaAPI/src/java/util/HashMap.java +++ b/vm/JavaAPI/src/java/util/HashMap.java @@ -566,7 +566,18 @@ public void putAll(Map map) { } } + @SuppressWarnings("unchecked") private void putAllImpl(Map map) { + if (map instanceof HashMap) { + // Straight from the source's slots: the entry iterator below builds + // an Entry per mapping, which copying a 5,000-key theme made garbage. + HashMap source = (HashMap) map; + for (int i = source.cn1FirstIndex(); i >= 0; i = source.cn1NextIndex(i)) { + put((K) NativeStorage.get(source.cn1KeysBlock, i), + (V) NativeStorage.get(NativeStorage.part(source.cn1KeysBlock, VALS), i)); + } + return; + } Iterator it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); diff --git a/vm/JavaAPI/src/java/util/Hashtable.java b/vm/JavaAPI/src/java/util/Hashtable.java index b4edc1bdd09..da37414a0ac 100644 --- a/vm/JavaAPI/src/java/util/Hashtable.java +++ b/vm/JavaAPI/src/java/util/Hashtable.java @@ -87,9 +87,27 @@ public void remove() { *

There are no node objects in this representation, so unlike the * chained layout this replaced -- where the Entry WAS the storage and cost * an allocation per mapping -- an Entry is built only when an iterator - * hands one out, and only for {@code entrySet()}. It keeps the table and + * hands one out from {@code entrySet()}. It keeps the table and * slot so {@link #setValue} still writes through. */ + /** + * Markers for key and value iterators, which {@link HashIterator#at} reads + * from the slot without building an Entry; never called. + */ + @SuppressWarnings("rawtypes") + private static final MapEntry.Type KEYS = new MapEntry.Type() { + public Object get(MapEntry entry) { + return entry.key; + } + }; + + @SuppressWarnings("rawtypes") + private static final MapEntry.Type VALUES = new MapEntry.Type() { + public Object get(MapEntry entry) { + return entry.value; + } + }; + private static class Entry extends MapEntry { private final Hashtable table; @@ -240,6 +258,23 @@ private class HashIterator implements Iterator { expectedModCount = modCount; } + /** + * The element at an occupied slot. A key or value iterator reads the + * slot directly; only an entry iterator builds an {@link Entry}. A + * theme load walks its tables' keys several times, and an Entry per + * key was 16,000 objects of garbage per launch. + */ + @SuppressWarnings("unchecked") + final E at(int idx) { + if (type == KEYS) { + return (E) NativeStorage.get(cn1Keys, idx); + } + if (type == VALUES) { + return (E) NativeStorage.get(NativeStorage.part(cn1Keys, VALS), idx); + } + return type.get(new Entry(Hashtable.this, idx)); + } + /** Advance past empty and tombstoned slots. */ final boolean advance() { long meta = NativeStorage.part(cn1Keys, META); @@ -266,7 +301,7 @@ public E next() { lastPosition = position; position++; canRemove = true; - return type.get(new Entry(Hashtable.this, lastPosition)); + return at(lastPosition); } public void remove() { @@ -448,11 +483,7 @@ public synchronized java.util.Enumeration elements() { if (elementCount == 0) { return (java.util.Enumeration) EMPTY_ENUMERATION; } - return new HashEnumIterator(new MapEntry.Type() { - public V get(MapEntry entry) { - return entry.value; - } - }, true); + return new HashEnumIterator((MapEntry.Type) VALUES, true); } /** @@ -614,11 +645,7 @@ public synchronized java.util.Enumeration keys() { if (elementCount == 0) { return (java.util.Enumeration) EMPTY_ENUMERATION; } - return new HashEnumIterator(new MapEntry.Type() { - public K get(MapEntry entry) { - return entry.key; - } - }, true); + return new HashEnumIterator((MapEntry.Type) KEYS, true); } /** @@ -659,11 +686,7 @@ public Iterator iterator() { if (this.size() == 0) { return (Iterator) EMPTY_ITERATOR; } - return new HashEnumIterator(new MapEntry.Type() { - public K get(MapEntry entry) { - return entry.key; - } - }); + return new HashEnumIterator((MapEntry.Type) KEYS); } }, this); } @@ -715,7 +738,7 @@ public E nextElement() { } int idx = position; position++; - return type.get(new Entry(Hashtable.this, idx)); + return at(idx); } return super.next(); } @@ -925,11 +948,7 @@ public void clear() { @Override public Iterator iterator() { return new HashIterator( - new MapEntry.Type() { - public V get(MapEntry entry) { - return entry.value; - } - }); + (MapEntry.Type) VALUES); } }, this); } diff --git a/vm/JavaAPI/src/java/util/ServiceLoader.java b/vm/JavaAPI/src/java/util/ServiceLoader.java new file mode 100644 index 00000000000..104a916d34e --- /dev/null +++ b/vm/JavaAPI/src/java/util/ServiceLoader.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util; + +/// The providers of a service, as in the JDK -- declared at build time. +/// +/// A device loads no classes at run time, so there is no class path to search. The +/// translator reads every `META-INF/services` file among the classes it translates and +/// generates [Registry#providers(Class)], which instantiates the providers a file +/// names, in the order it names them; a provider that is not among the translated +/// classes is left out. A service with no file has no providers. The class loader +/// argument is accepted and ignored: there is only one. +public final class ServiceLoader implements Iterable { + private final Class service; + private List providers; + + private ServiceLoader(Class service) { + this.service = service; + } + + public static ServiceLoader load(Class service) { + return new ServiceLoader(service); + } + + public static ServiceLoader load(Class service, ClassLoader loader) { + return new ServiceLoader(service); + } + + public static ServiceLoader loadInstalled(Class service) { + return new ServiceLoader(service); + } + + public void reload() { + providers = null; + } + + @SuppressWarnings("unchecked") + public Iterator iterator() { + if (providers == null) { + Object[] found = Registry.providers(service); + List l = new ArrayList(found.length); + for (int i = 0; i < found.length; i++) { + l.add((S) found[i]); + } + providers = l; + } + return Collections.unmodifiableList(providers).iterator(); + } + + public String toString() { + return "java.util.ServiceLoader[" + service.getName() + "]"; + } + + /// Replaced by the translator with the providers of every `META-INF/services` + /// file it was given; this body is what an application with none of them gets. + static final class Registry { + private Registry() { + } + + static Object[] providers(Class service) { + return new Object[0]; + } + } +} diff --git a/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicIntegerArray.java b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicIntegerArray.java new file mode 100644 index 00000000000..7e3108a1596 --- /dev/null +++ b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util.concurrent.atomic; + +/// An array of `int` updated atomically, as in the JDK. +/// +/// Every access goes through this class, so one lock per array makes each operation +/// atomic, the way [AtomicInteger] does it. +public class AtomicIntegerArray { + private final Object lock = new Object(); + private final int[] array; + + public AtomicIntegerArray(int length) { + array = new int[length]; + } + + public AtomicIntegerArray(int[] source) { + array = new int[source.length]; + System.arraycopy(source, 0, array, 0, source.length); + } + + public final int length() { + return array.length; + } + + public final int get(int i) { + synchronized (lock) { + return array[i]; + } + } + + public final void set(int i, int newValue) { + synchronized (lock) { + array[i] = newValue; + } + } + + public final void lazySet(int i, int newValue) { + set(i, newValue); + } + + public final int getAndSet(int i, int newValue) { + synchronized (lock) { + int old = array[i]; + array[i] = newValue; + return old; + } + } + + public final boolean compareAndSet(int i, int expect, int update) { + synchronized (lock) { + if (array[i] == expect) { + array[i] = update; + return true; + } + return false; + } + } + + public final boolean weakCompareAndSet(int i, int expect, int update) { + return compareAndSet(i, expect, update); + } + + public final int getAndAdd(int i, int delta) { + synchronized (lock) { + int old = array[i]; + array[i] = old + delta; + return old; + } + } + + public final int addAndGet(int i, int delta) { + return getAndAdd(i, delta) + delta; + } + + public final int getAndIncrement(int i) { + return getAndAdd(i, 1); + } + + public final int getAndDecrement(int i) { + return getAndAdd(i, -1); + } + + public final int incrementAndGet(int i) { + return getAndAdd(i, 1) + 1; + } + + public final int decrementAndGet(int i) { + return getAndAdd(i, -1) - 1; + } + + public String toString() { + StringBuilder b = new StringBuilder("["); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + b.append(", "); + } + b.append(get(i)); + } + return b.append(']').toString(); + } +} diff --git a/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java new file mode 100644 index 00000000000..6b60ab3dbba --- /dev/null +++ b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util.concurrent.atomic; + +/// Atomic access to a volatile `int` field of a class, as in the JDK. +/// +/// Resolved by the translator exactly as [AtomicReferenceFieldUpdater] is: see that +/// class for how, and for what a call with non-constant arguments does. Only +/// [#get], [#set] and [#compareAndSet] are generated per field; every other operation +/// is a compare-and-set loop over them. +public abstract class AtomicIntegerFieldUpdater { + /// For the subclasses the translator generates. + protected AtomicIntegerFieldUpdater() { + } + + /// Resolved by the translator when every argument is a constant; reaching this + /// method means it could not be. + public static AtomicIntegerFieldUpdater newUpdater(Class tclass, String fieldName) { + throw new UnsupportedOperationException("Field updaters must name their class and field as constants: " + fieldName); + } + + public abstract boolean compareAndSet(T obj, int expect, int update); + + public boolean weakCompareAndSet(T obj, int expect, int update) { + return compareAndSet(obj, expect, update); + } + + public abstract void set(T obj, int newValue); + + public void lazySet(T obj, int newValue) { + set(obj, newValue); + } + + public abstract int get(T obj); + + public int getAndSet(T obj, int newValue) { + int prev; + do { + prev = get(obj); + } while (!compareAndSet(obj, prev, newValue)); + return prev; + } + + public int getAndAdd(T obj, int delta) { + int prev; + do { + prev = get(obj); + } while (!compareAndSet(obj, prev, prev + delta)); + return prev; + } + + public int addAndGet(T obj, int delta) { + return getAndAdd(obj, delta) + delta; + } + + public int getAndIncrement(T obj) { + return getAndAdd(obj, 1); + } + + public int getAndDecrement(T obj) { + return getAndAdd(obj, -1); + } + + public int incrementAndGet(T obj) { + return getAndAdd(obj, 1) + 1; + } + + public int decrementAndGet(T obj) { + return getAndAdd(obj, -1) - 1; + } + + /// Equality, for the branch-free compare-and-set the translator generates where + /// every thread shares one host thread (JavaScript). + protected static boolean cn1Same(int a, int b) { + return a == b; + } + + /// `c ? a : b`, for the same generated code. + protected static int cn1Select(boolean c, int a, int b) { + return c ? a : b; + } +} diff --git a/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicLongArray.java b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicLongArray.java new file mode 100644 index 00000000000..1d07a8b3100 --- /dev/null +++ b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicLongArray.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util.concurrent.atomic; + +/// An array of `long` updated atomically, as in the JDK. +/// +/// Every access goes through this class, so one lock per array makes each operation +/// atomic, the way [AtomicLong] does it. +public class AtomicLongArray { + private final Object lock = new Object(); + private final long[] array; + + public AtomicLongArray(int length) { + array = new long[length]; + } + + public AtomicLongArray(long[] source) { + array = new long[source.length]; + System.arraycopy(source, 0, array, 0, source.length); + } + + public final int length() { + return array.length; + } + + public final long get(int i) { + synchronized (lock) { + return array[i]; + } + } + + public final void set(int i, long newValue) { + synchronized (lock) { + array[i] = newValue; + } + } + + public final void lazySet(int i, long newValue) { + set(i, newValue); + } + + public final long getAndSet(int i, long newValue) { + synchronized (lock) { + long old = array[i]; + array[i] = newValue; + return old; + } + } + + public final boolean compareAndSet(int i, long expect, long update) { + synchronized (lock) { + if (array[i] == expect) { + array[i] = update; + return true; + } + return false; + } + } + + public final boolean weakCompareAndSet(int i, long expect, long update) { + return compareAndSet(i, expect, update); + } + + public final long getAndAdd(int i, long delta) { + synchronized (lock) { + long old = array[i]; + array[i] = old + delta; + return old; + } + } + + public final long addAndGet(int i, long delta) { + return getAndAdd(i, delta) + delta; + } + + public final long getAndIncrement(int i) { + return getAndAdd(i, 1); + } + + public final long getAndDecrement(int i) { + return getAndAdd(i, -1); + } + + public final long incrementAndGet(int i) { + return getAndAdd(i, 1) + 1; + } + + public final long decrementAndGet(int i) { + return getAndAdd(i, -1) - 1; + } + + public String toString() { + StringBuilder b = new StringBuilder("["); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + b.append(", "); + } + b.append(get(i)); + } + return b.append(']').toString(); + } +} diff --git a/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicLongFieldUpdater.java new file mode 100644 index 00000000000..1ae2d627944 --- /dev/null +++ b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util.concurrent.atomic; + +/// Atomic access to a volatile `long` field of a class, as in the JDK. +/// +/// Resolved by the translator exactly as [AtomicReferenceFieldUpdater] is: see that +/// class for how, and for what a call with non-constant arguments does. Only +/// [#get], [#set] and [#compareAndSet] are generated per field; every other operation +/// is a compare-and-set loop over them. +public abstract class AtomicLongFieldUpdater { + /// For the subclasses the translator generates. + protected AtomicLongFieldUpdater() { + } + + /// Resolved by the translator when every argument is a constant; reaching this + /// method means it could not be. + public static AtomicLongFieldUpdater newUpdater(Class tclass, String fieldName) { + throw new UnsupportedOperationException("Field updaters must name their class and field as constants: " + fieldName); + } + + public abstract boolean compareAndSet(T obj, long expect, long update); + + public boolean weakCompareAndSet(T obj, long expect, long update) { + return compareAndSet(obj, expect, update); + } + + public abstract void set(T obj, long newValue); + + public void lazySet(T obj, long newValue) { + set(obj, newValue); + } + + public abstract long get(T obj); + + public long getAndSet(T obj, long newValue) { + long prev; + do { + prev = get(obj); + } while (!compareAndSet(obj, prev, newValue)); + return prev; + } + + public long getAndAdd(T obj, long delta) { + long prev; + do { + prev = get(obj); + } while (!compareAndSet(obj, prev, prev + delta)); + return prev; + } + + public long addAndGet(T obj, long delta) { + return getAndAdd(obj, delta) + delta; + } + + public long getAndIncrement(T obj) { + return getAndAdd(obj, 1); + } + + public long getAndDecrement(T obj) { + return getAndAdd(obj, -1); + } + + public long incrementAndGet(T obj) { + return getAndAdd(obj, 1) + 1; + } + + public long decrementAndGet(T obj) { + return getAndAdd(obj, -1) - 1; + } + + /// Equality, for the branch-free compare-and-set the translator generates where + /// every thread shares one host thread (JavaScript). + protected static boolean cn1Same(long a, long b) { + return a == b; + } + + /// `c ? a : b`, for the same generated code. + protected static long cn1Select(boolean c, long a, long b) { + return c ? a : b; + } +} diff --git a/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicReferenceArray.java b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicReferenceArray.java new file mode 100644 index 00000000000..e2a8b3494f3 --- /dev/null +++ b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util.concurrent.atomic; + +/// An array of references updated atomically, as in the JDK. +/// +/// Every access goes through this class, so one lock per array makes each operation +/// atomic, the way [AtomicReference] does it. +public class AtomicReferenceArray { + private final Object lock = new Object(); + private final Object[] array; + + public AtomicReferenceArray(int length) { + array = new Object[length]; + } + + public AtomicReferenceArray(E[] source) { + array = new Object[source.length]; + System.arraycopy(source, 0, array, 0, source.length); + } + + public final int length() { + return array.length; + } + + @SuppressWarnings("unchecked") + public final E get(int i) { + synchronized (lock) { + return (E) array[i]; + } + } + + public final void set(int i, E newValue) { + synchronized (lock) { + array[i] = newValue; + } + } + + public final void lazySet(int i, E newValue) { + set(i, newValue); + } + + @SuppressWarnings("unchecked") + public final E getAndSet(int i, E newValue) { + synchronized (lock) { + Object old = array[i]; + array[i] = newValue; + return (E) old; + } + } + + public final boolean compareAndSet(int i, E expect, E update) { + synchronized (lock) { + if (array[i] == expect) { + array[i] = update; + return true; + } + return false; + } + } + + public final boolean weakCompareAndSet(int i, E expect, E update) { + return compareAndSet(i, expect, update); + } + + public String toString() { + StringBuilder b = new StringBuilder("["); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + b.append(", "); + } + b.append(get(i)); + } + return b.append(']').toString(); + } +} diff --git a/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java new file mode 100644 index 00000000000..46d44a197cd --- /dev/null +++ b/vm/JavaAPI/src/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util.concurrent.atomic; + +/// Atomic access to a volatile reference field of a class, as in the JDK. +/// +/// The device has no reflection, so [#newUpdater(Class, Class, String)] cannot find a +/// field by name at run time. The translator resolves the call instead: wherever the +/// class, the value class and the field name are constants -- which is how javac, +/// kotlinc and atomicfu all emit it, in a static initializer -- it replaces the call +/// with an instance of a subclass it generates for that one field. The generated +/// [#compareAndSet] is a hardware compare-and-swap on the field, so it is atomic with +/// respect to plain volatile reads and writes of the field as well as to other +/// updaters. A call whose arguments are not constants reaches the method below and +/// throws. +public abstract class AtomicReferenceFieldUpdater { + /// For the subclasses the translator generates. + protected AtomicReferenceFieldUpdater() { + } + + /// Resolved by the translator when every argument is a constant; see the class + /// description. Reaching this method means it could not be. + public static AtomicReferenceFieldUpdater newUpdater(Class tclass, Class vclass, String fieldName) { + throw new UnsupportedOperationException("Field updaters must name their class and field as constants: " + fieldName); + } + + public abstract boolean compareAndSet(T obj, V expect, V update); + + public boolean weakCompareAndSet(T obj, V expect, V update) { + return compareAndSet(obj, expect, update); + } + + public abstract void set(T obj, V newValue); + + public void lazySet(T obj, V newValue) { + set(obj, newValue); + } + + public abstract V get(T obj); + + public V getAndSet(T obj, V newValue) { + V prev; + do { + prev = get(obj); + } while (!compareAndSet(obj, prev, newValue)); + return prev; + } + + /// Identity comparison, for the branch-free compare-and-set the translator + /// generates where every thread shares one host thread (JavaScript). + protected static boolean cn1Same(Object a, Object b) { + return a == b; + } + + /// , for the same generated code. + protected static Object cn1Select(boolean c, Object a, Object b) { + return c ? a : b; + } +} diff --git a/vm/JavaAPI/src/java/util/concurrent/locks/LockSupport.java b/vm/JavaAPI/src/java/util/concurrent/locks/LockSupport.java new file mode 100644 index 00000000000..6b0c1c32594 --- /dev/null +++ b/vm/JavaAPI/src/java/util/concurrent/locks/LockSupport.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util.concurrent.locks; + +import java.util.WeakHashMap; + +/// Thread parking, with the JDK's permit semantics: [#unpark(Thread)] before +/// [#park()] makes that park return at once, and a park may also return spuriously +/// or because the thread was interrupted, whose status it leaves set. +/// +/// Each thread gets a monitor of its own the first time it parks or is unparked, +/// kept in a weak map so a finished thread does not hold it. +public class LockSupport { + private static final WeakHashMap PARKERS = new WeakHashMap(); + + private LockSupport() { + } + + private static final class Parker { + boolean permit; + } + + private static Parker parker(Thread t) { + synchronized (PARKERS) { + Parker p = PARKERS.get(t); + if (p == null) { + p = new Parker(); + PARKERS.put(t, p); + } + return p; + } + } + + public static void unpark(Thread thread) { + if (thread == null) { + return; + } + Parker p = parker(thread); + synchronized (p) { + p.permit = true; + p.notifyAll(); + } + } + + public static void park() { + block(0, true); + } + + public static void park(Object blocker) { + park(); + } + + public static void parkNanos(long nanos) { + if (nanos > 0) { + // Object.wait has millisecond resolution here; round up so a short park + // still waits rather than spinning. + block((nanos + 999999L) / 1000000L, false); + } + } + + public static void parkNanos(Object blocker, long nanos) { + parkNanos(nanos); + } + + public static void parkUntil(long deadline) { + long millis = deadline - System.currentTimeMillis(); + if (millis > 0) { + block(millis, false); + } + } + + public static void parkUntil(Object blocker, long deadline) { + parkUntil(deadline); + } + + public static Object getBlocker(Thread t) { + return null; + } + + private static void block(long millis, boolean forever) { + Thread self = Thread.currentThread(); + Parker p = parker(self); + synchronized (p) { + if (!p.permit && !self.isInterrupted()) { + try { + if (forever) { + p.wait(); + } else { + p.wait(millis); + } + } catch (InterruptedException e) { + // park reports an interrupt by returning with the status still set. + self.interrupt(); + } + } + p.permit = false; + } + } +} diff --git a/vm/JavaAPI/src/java/util/jar/JarFile.java b/vm/JavaAPI/src/java/util/jar/JarFile.java new file mode 100644 index 00000000000..f54ee185ae4 --- /dev/null +++ b/vm/JavaAPI/src/java/util/jar/JarFile.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.util.jar; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; + +/// Declared so that code reading a jar from its class path links. An application has no +/// class path here, so opening one always fails. +public class JarFile { + public JarFile(String name) throws IOException { + this(name, true); + } + + public JarFile(String name, boolean verify) throws IOException { + throw new IOException("no jar files on this platform: " + name); + } + + public InputStream getInputStream(ZipEntry entry) throws IOException { + throw new IOException("no jar files on this platform"); + } + + public void close() throws IOException { + } +} diff --git a/vm/backend/gen/META-INF/cn1/orm-enhanced-dependencies.list b/vm/backend/gen/META-INF/cn1/orm-enhanced-dependencies.list new file mode 100644 index 00000000000..e69de29bb2d diff --git a/vm/tests/pom.xml b/vm/tests/pom.xml index fd3537d3773..d74b0e780e0 100644 --- a/vm/tests/pom.xml +++ b/vm/tests/pom.xml @@ -89,6 +89,15 @@ + + + + google + https://maven.google.com + + + @@ -107,6 +116,53 @@ test + + + copy-kmp-runtime + generate-test-resources + + copy + + + ${project.build.directory}/kmp-runtime + + org.jetbrains.kotlinkotlin-stdlib2.4.21 + org.jetbrains.kotlinxkotlinx-coroutines-core-jvm1.11.0 + androidx.compose.runtimeruntime-desktop1.12.1 + androidx.compose.runtimeruntime-annotation-jvm1.12.1 + androidx.collectioncollection-jvm1.5.0 + androidx.annotationannotation-jvm1.9.1 + org.jetbrainsannotations23.0.0 + + + + + copy-kmp-compiler + generate-test-resources + + copy + + + ${project.build.directory}/kmp-compiler + + org.jetbrains.kotlinkotlin-compiler-embeddable2.4.21 + org.jetbrains.kotlinkotlin-compose-compiler-plugin-embeddable2.4.21 + org.jetbrains.kotlinkotlin-stdlib2.4.21 + org.jetbrains.kotlinkotlin-script-runtime2.4.21 + org.jetbrains.kotlinkotlin-daemon-embeddable2.4.21 + org.jetbrains.kotlinkotlin-build-tools-api2.4.21 + org.jetbrains.kotlinxkotlinx-coroutines-core-jvm1.8.0 + org.jetbrainsannotations13.0 + org.jetbrains.kotlinkotlin-reflect1.6.10 + + + + source directory supplied. --> org.apache.maven.plugins maven-dependency-plugin @@ -581,7 +580,13 @@ runtime - org.jetbrains.kotlin,org.jetbrains + META-INF/** ${project.build.directory}/translation-libs diff --git a/docs/demos/common/src/main/snippets/developer-guide/kotlin-multiplatform-interop.kt b/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSnippets.kt similarity index 62% rename from docs/demos/common/src/main/snippets/developer-guide/kotlin-multiplatform-interop.kt rename to scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSnippets.kt index e758bc7fcfc..6a9d58d0abf 100644 --- a/docs/demos/common/src/main/snippets/developer-guide/kotlin-multiplatform-interop.kt +++ b/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSnippets.kt @@ -20,24 +20,32 @@ * Please contact Codename One through http://www.codenameone.com/ if you * need additional information or have any questions. */ -// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. +package com.codenameone.examples.hellocodenameone.kmp -// tag::kotlin-multiplatform-interop-kotlin-001[] -plugins { - id("org.jetbrains.kotlin.plugin.compose") version "2.2.10" -} -// end::kotlin-multiplatform-interop-kotlin-001[] +import com.codename1.compose.ComposeForm +import com.codename1.compose.ComposeView +import com.codename1.system.Lifecycle +import com.codename1.ui.Form +import com.codename1.ui.Label +import com.codename1.ui.layouts.BorderLayout + +// The developer guide's entry-point examples (Kotlin-Multiplatform-Interop), +// kept here so they compile with the rest of the Compose screens. Nothing +// calls them. // tag::kotlin-multiplatform-interop-kotlin-002[] class MyApp : Lifecycle() { override fun runApp() { - ComposeForm { App() }.show() + ComposeForm { LayoutScreen() }.show() } } -// Or beside other components: -val form = Form("Mixed", BorderLayout()) -form.add(BorderLayout.NORTH, Label("Codename One")) -form.add(BorderLayout.CENTER, ComposeView { Schedule() }) -form.show() // end::kotlin-multiplatform-interop-kotlin-002[] +internal fun guideMixedForm() { + // tag::kotlin-multiplatform-interop-kotlin-003[] + val form = Form("Mixed", BorderLayout()) + form.add(BorderLayout.NORTH, Label("Codename One")) + form.add(BorderLayout.CENTER, ComposeView { ListScreen() }) + form.show() + // end::kotlin-multiplatform-interop-kotlin-003[] +} diff --git a/scripts/mac-catalyst/screenshots/KmpCompatLayout.png b/scripts/mac-catalyst/screenshots/KmpCompatLayout.png new file mode 100644 index 0000000000000000000000000000000000000000..a9d352f0bb64101ff96c74ede73c6171610b4e31 GIT binary patch literal 21328 zcmeHuby!s2*YBAjq(Ny(MG29X9+)waE)fAKB?Kg-J5)lHP(VOhx}*f8QKY21JBIFt zVeUaue7`@x_q~7I=ef^&AMu$vGvhjY?Y;Jj&suAr7YcF`gm`E1001DAynRCn05HLi z&`caCcoE=uISgJPR!S1rfDg^Iv*4HK`u8LaWMzR%;Ab2F3vvd)I3fZ60g$r**7wf< zAO)fM^;rqRdQ1b3>236#0Z0v~gRi5%+u-Zy`;RL|EaYUwSm;k`OduBH=Vxf<(QU4{ z@C)Dt*W$LC6#(Fq9=#yIyGSbVM_QvB@^`@N(IUVP zhG2xEaVBA6vRGsa2*~jpq7oP-B>C+Z=oxMR(>lkm^yh8h08{`0@Y`3+3pxPK%!3F! z@{@760V?3PVR3F(0wk@~0?U_9CZGdw1IJT2niNT(JA^UzrRlKBugNG>0y@CSm7e z(5<|Dws2;?M$oNzI_nNoxHs#;Sq%5jw>)5Eb$e$52YN&`l#eHZnOlxb%1YQ+7TOOr z`LL6f&7{kSLU?Km0F%0_s!F#~7Y$c&^;~dQ^woc|;XGoKpBpBH-nfIPyiNBZ5FZ=B zaECgu25xS{kz5V}DaT7t8?lNH;kkoWJi}^?&Ha5u=ZS9x_mx1dRJ&(0kI957>-M4> zq^6Mx0UeavYuYwU^Ntwq5XATC4n(hq!f5XNn65|0a#6;$g=dZbzb-i1x1v&!0C>BJhsgo*V*STD@q?#VolO9o z;vIIj_|C1ooj)v*M!q`6Avhzd{{Ac(WBk)2f$gR@zx&6Gnl3j@s0HQog|ia^4)+eo zM?U3pgpzqQYTI=0{8y3D$QK=tXLO`)f!@&Yin)yXUFm4TV^utoLSs8DnqN&csHpwF zhzsO=Ry+P!s~M3{aIYrVw)q0y{NRiYV67=IuB%4z3%k|lUHvK7z32$FceQ!UCWgGA zP}j0|*vp^jYjCoC=e+t)ky*mmS$}W_Rh^L-i`T77E$AQH8J_3>Gs(4Own~IB62Gi? z!z*P9e<}Z+bNMayV{2~40@buedC8Fe;t$S0wsSg5#KiLOjp4IngFRBryek2Qvfa&k zAFlkaZ%3RbOg- znxX12FJCx{#M2FMC!+c3RVd^CVZ-|Rje-z7_<=dN$=|Y4u2-AIghJJ{U;RU6^z|bJ zA--2YvLo}2IimQdI;&I5W3w7>f&;aTb8&fp6iYo1y5s-wPWf*L6f&$l@X+<}RL-1H zKEU9+{h~r)`H{aHT<87*IfXA1_I-_62AkY$$S7X&WWaB+Od&P_hYC0>Nw|M$KlLOK zRc_+{IWB6uX??`{6;VCjaQ->)X|{Kyp;UjxzE7@@BmaJ^#vpqScT55RqPZ~N^0MG0 z;3>3Y5dfegI+@>bz?ODY&qtpG=^ic%4v5a`D;H-!2Ln|6B62VBWa{ov9{d|vyN8;{ ztaHR<9DqeaSW8pY?^vNy8<(3^yOT=cIS_buts=Ve2_bd0<_S|W1x8KAOLfYd4zGC; zx)^}pt(^GVnaSJj`oGPLtS%D~4B07MX`&DSZpdn&D7Csde!~ntS7TE=9d$7fR}L7e zaP4t^zm>HVu@T0Jrm&>MaA&|sJDEEImx3Dz9(}1=*E(%*vR=Fdd@`c~e>;GH;I|)x zAAZmCSf z(wqRE$+AC_*$&sL$^Ud;WD28c(C^6MWEv1$DgavCtLpd6>_iU&xBrFAfx%GhXJ`VI zDE(i+n>IgDvBwEC9R3$wM!H09VxDZ-{|vze!l7;ltJ)O@Yqg2eZb=n;%l-N0-5I<4 zTXa&|4yFwi=);(g=mB%|6VnpP-uXglxAwE96!+Z3M%oJ->7idJg?c)2_mAL$0}}{= zVgwIMcjaeaumb@HxBd4mz64#5adqvKeo5)(IQAv+y=|D(Mskd+nAJ2aliX&6 z{jvN-*%7m%*)0}qV(F2E#lqU|6fNYl9ERkq(gOXbH>%fEYTRJEwG3`W1r$n7F55$% zZSh)Z+F7dyyDyd5v#iGUm$}Lh+UGatu0--VU3F9zE8cWWX<^#mW{}gK%{{`;89()j z?5?{yItWX8uE+FF`QyZrrBC}yO8O>UDf^8+ipUo9odmnrF>Z&bGCOzzmO6X8zv}Q# z7PRtR7fm#suwzF}rs{ARH#~~h0UN0`4n_2E4iN&LMdfDk?obcV^Wp8+Fx0u>$SXsz zJ}_G7pvShhZOhlU+BHS&B1H73)3Zt)&&nM;pCciym6YlJgjfSB_O%Bi2N1MJ(SE+kW673b@$} zZfu5ok0uXF7Y$Aqj#TV?Ne{OJGej3hc&Xtwq+pDlj!%TE`$ocG-_H0;&G~M#r}*!O zv=_DWjZv!)llj>0gR8|0zW>t+vt|G`V|0a~W__hi%~4KlZt}G7_5SpSQbP_ct>>y7 zXkxYQml>lZCYV7{^va8?IW7$bWWX$UFmJAZk$)=}eFe2xIBaN)7jkhRkk;&ATcfv7 zV*6CzE2#D693eVy`RT>B@>>7scjBf3va;d4L0nSK%sGwGWjYJ{467V2v2T(di&;)y z@FQWNidf!njSq*oXSA0ZrT67c^UWJ94SEjRh6z99j7snyDcLqDj&dZ9=VxYaiYfG= zA#-jswUtB#d$I!7OV)0S?>3h{W!_1DyV#x4x6@Ejd^Jt2+?hXl<xvhZIy z_9WSX>E_+%T*bxu4V0{z+Jz1WJ8aA4>yW5~^aUJznk%O5Zv-Ovjq1Y<2mu>o!AC>6 zbPN3HJ!99hxrX9DXKEBalYGXP)Y6xS9BqqF_JZ^0UM;H`oa)`{3k}$qA9=54(#%fx z`N(s}CV&nKR7CCetdC&O^q8j%6VkmzK55lbju^XbLI8^qb}?Ig=Lgf5z9@z2&%LR^ zjHB4oojK(GxH5aA{UZCfVdXeo6f;>;2>ac1wRlcRohjdZQ;ouk&1q4`?Kx=u0gIY3cRk9!IxngoZt z-#gG?7pc_|01U3&<&ItIq%3lx7NfU=YB-F0(pqZx)4HbbhgEz{agg%jMijCw3F9Rr zE{YkAf9#Z3^MO{%3J&(CURO1H#{Kd|&;z}wxfqVr*;&&nY*`w?$2IqzzoD8U;7Has zwvR?b(NqXjlQO2J$WXwU#TyX!+=vOKuNc*RLnHaM?{7A|z^saHO_kA`*pfD3Upk5%FD*>!rBhJ2@3 z3G)Kvr@?xM$+6lOD)^L~@iXQFNmZzAF?aGpwo;71L$^wBJL)igGm{SK6gz4fxw1Xw zZ9YOT7$Q^tv}B0nVl&eh74{xh7u9eVrwRjG5^`atj?VI|iYE-TUun~h$ipoH z?+&sMR+VK!wC(zoORa2%dLr|)KBsNkgpY}_$!aytaLw{}PfXSjJazRpjUDEiw=uh+u^|DSvo; zF=^Sbz-pn_Xzu1E1ME!dWW%pf`o^+Q;Nn7^VYFdrmAAY?1aD*uLCgkFsUS#`x1Sv+ zuRUC-Zu&O7`x>C*6GhDy+%Hp2z4lc(!;m)0l*&DM&Sm2@#Q8EYIUN6^JW*e^Yl8{~ zz_cx+^}uOvXTZFSy{s}3Yw5Oy#%R&&c($*?w3{w_^|v7I?;_&tx`Sj`W5Z>?HsKjy zDyDWoJ7(%{+l6vUD9Qn&wMo4cBajLM8-bi$%ugXVEb086l_LBjh_71V+nHnLAm7^lnf~ISdNtgr%6p6!KKIa=BJn$u=V>E9ii<1BDuLA|; zqIbM#C?lCU0#(8;w#-_5yVGv{aIRA=vRRs|{MKui0S-Ifuk`$1!~!1$N6|3DvKk+` zkO7~PA(#1lg!q*phEm_IxdWB=+0N?P%~|$e*n$b`2}P`Od25u|l4`vF8YX7S>*0MT zh#%ksE%b z+zsEVYUPhVyc2lmpvqgU3gPq_Xf0rxV(Wr+7_d69Ask9WW)Ee;;&_wZ7-$>BDU2w; zOg;r%Qhk8EuEgqJe~$Kb+PiSRR}Sk6ydfg-{dcwqd=0UIqzgO~F~WOoWU&%x5nu%( zbmKJkZ0Z~8>f}@@^0cIvHzDq(#X}=OMznGb35CdW{DZj|@j4SGx#gUMg zi`xf(u(YL(y6V!7Z>mKEScyw63&cRl=;q^wiLGud)sxVCQvR&g>h^_7-rQ>5y(WB> z__Ed8Pp7`@)%d-$`&7s$NZ77nr3NMrtg}Bcyuv-mt@&M;Z1iBFal*aHmlBHM*i}r~ zublasqACij*HeQ-f-7Vgl6{bT&M6C$_@x>vi&KX8<^|qwT)^Na_oi4f?RmkV7_V%d z%XO1Gk-O^Pv5ZSgudVgAyQzI_LCZ5#Z`3VT83&)YN@3SYrNbz{63}8#^hM3RtqtxN ziG0hUQN8#Sbl0mg(7gLpL&!dT0Y-yto{kx18moZ!Y8`iJgKI77$p(&sA#gC2Uj~gfI zU@T2mID-~I5b0Qur;x-FIX9nS<%GON%X3S3f7G4I03kd2mZ`B(f4bteoZB+D&c6-( z`Y4EEhb-9oX_+HJgD_*;-d_8${YoEL1)<#gm>;5q6gT|mS;tfljnu^pfed3!P z8(1P$ySJhY#df`&P9LZ}ql|V7_qHx_A`#PQTwuoTtf!ydwTV*jTn?4q~)vRBs8rhHxY#^=^Q-NzX}m0Z88 zw7pJMsY0lnS#*>@o`BrCM1WuH<^`-({!*TUyFM=QaK~!J4{@k#OV=h+eU$1n-!}=F zY{_OVyet;oU$fCKOYyH*6Syg2cH>z=&SuWCr_?VqOdGX*}*if{UQnPq_E{9qCo z`Y&V-)VgA9pxZhwPM!x->OZwKDA~>y=lmQj7NPYo$um;_`DVh;!5~<^U_azftqskD zM>91idAtMLpUI*^Pj?3?{i^)@&l248^Vx1a4$#Ul!){7mzJvI{0dh7KvRqlgpUspb zp?l1e-Ge=3$QMCVE}3z@_dtMVE(%ry@0kIFf0LN zJi`ml^pGV^4;Ze%*}l7$?20g7%C-UpMq60hCVpym3eW)^>`m9N|>xgzTNi zbZ0HOVdZhSwf+&~WLNMoIT5ohR4$6k++@N4*(P9?^-G;VU8_IM4eYQXey9J@{bFIU z>nAg*->|ceV}mpaIp{_+g)Klc1G;Q z9=A*Xg2dh?TJQ6lj8(>FSI5i04fH{l@dZR@KUCX~mdCh`RQ{$mSNY0ZM)*3FV?_bq z<#w%8M6?dE`_JqK?;3xbq-K$G<}=)SDyZe=1XeN>nKqDKZYAM|2Yd7Imz^2Ec^cM0 z5|1y~?&!if2H}I^wfdH5espyE6l=aTn9_3y4!I)f?0PbXMp4@}nmaFwOGaPObvti% z9XhO2nBJ@KEOs6l&O$ct>VbJ9!7u~d)oY5Jy*p`c^SZZxOGJr9{kVKly#)x!-y)ke z>)sL^Xlq5U*Mn)TP^)`w%)JWNhVIM(C$ZM#4Nd)MhRyZ>_~92q4^{xSy?}4n9Esi< zSZ;|y*Uuu=~g)zS^bmRMvWh5YmdhD)~Rn<5HS((Yv%_!VOUM0;OjS>OX})uhk7his5y zlO*kRLD#)V>E8pb*{(rvIr`t0C0~oUyUk0-8p0`rDeOVJwj|0BNUh~E)l~iREh%|> z7OF1~ep$2lO$pxPmF}=@0kfS~nloFy+weV|g<7zFCIaSHeW;G9^l<$fQo;^v1Cpf4 zWgFur2}Ez|RZF(o&f@HE(>*{naFsPvI?UF+4$3f0J7a{5Dy(f6%5+(+=G@zA5*%rY zFLini3^GnvDU0Gs3dDNe9v1%P-kGLFFpcExKTpMUx;Z2{2S5asxaVwvu(?n*h zfqQrATG8W+c-zBedj;k_FL+#|2W2ZhWNHjakI7Zb`^g2yE*VNsqw?Og*)3sPS5&a1 z>dXa)JJ~YKG@O$PaY5x5zNY{+3Y~NH6fwO%Ec!v2>HL;S&tsYV7CJ9od4Hv9+RxV{>~h@1(qW>L8{ zIAdRm)`=FO+8lhDQ3{rN#77FAHCDw5>(9BG5m|`72az#Y6vM$%c#gLO1o{i{+ukp< zRYp1b^%W}D?hCzEN$*Z}oPT-(3BZUz2y|wE)y@FyezxxL_zE}l7}2(^f!MkR6^`L( zg;tbTqIAd<1i&+aVvO+C4P`Aq3}II>?2K$%-Ls*|QQfmi#7W{c>;Htszdh=4o!X1# zLN*UO_2rSmWd#oav7c2Y!fbpF58(25^~`bDyS}Yn_*i0>9u*8ZB%~3K&qdPT%!(#n zRW9C}VZykL--azSr0YslTMUy-mG#~)-eLqSxXzOU_Iu&C!WeIi)NxaC9oFzhi1ns( zk%m9=11c97+p!uVncJSS-7@JhJ8y5n;^E=eEoj{O5v&FUk~~T%N!z-=@sAw`F4DTd)i;qjW7@zfpFq?!!g6`(K9(bk-(#q6q;fA|<^b z_GoGz-|#(|Z|E3cB}*Q1-i8bZfRW1FVYIno-1~SXmPJFryy{##2Ef71;;p-X6~oJV z)*7y1nKvFEz`Uwp+36i6y48B!?8XX5y0fFah{33RATD-Bv4>1|8+=+Lj6BzaX3f2JQf!5^h zD(f&7$i0?VbjFSpGeGTB4YUDVTAU#>Gf#5Nr5LUxKxW)j8#k&qk7U5Lm{sHaU^i(8T*eAc?|hT?hwmJHUwA+%HclkBf0x(L;~W@CIjZH z<5{YOZD9TPHY2RFp-^FksbDM`8|vK2CVv+AGDOV!$hWd88Fl&or^}ARq46U&wL@V1 zQ_t1IYN1;B2ndRO+I%fJc7k&}S~1^H0XwYp-yFq5V&-DzKi3|}M+ii)XtQ;v#ub_l z$=Nhtj>RQS`k-Fq9{_0@<9`U|W=~TlcFEfAJA6Q|$mnf>=X~aL z(Rq}7*pn8IfbBRJB}~%DodX8{Is#W;r*iwWb9+<@OSmKt>sVlSDrGUE38r6T6tKV} zVXIB%p@+|XTG;kho~=47Zc8v=d<(*bt>KgLx%Ix3z^}W2iQx#P4D$Ri=-_tJJ|%ey zqBI1~(kzk+3z+6oT(vUy2){iGw>hdu{M2w-Fzv}bFdC!Xqex;qOrzxU4AZ}}qe19t z)NT5c2qdo~;|2~;5XSYiPq%)%`@z3JSI#67qr4LweGxn%_NT*MC74*sB_|VBkoix* zJgEVUUft{8brD|;f^Sy$@VjlWxLt7{+Kt!g8N9}$FBS0{O3p=SVY!3Q)D!NY z5wE`Zf;74Mg&aq7%)Xe@)~QQt*XXxFR0M`!SMw4%ZP#6ub9QXmeQ7H55rU?h3w3QQ z@~#@A60W};LId05RDh*L2@>pFF~vBPgHHcIcBJAdXZGP9MrCdyd&CuEGw{gYeBsH0 zRVi+p0XhcwO!I2ULhdRS7qJMQMI_h+YmVAq1zU3m>7u~nnw}70ypOb|kv6p(90Byg zyI}LdxGSYZ=kuB3*CLovm$|85nD>n$W8C0R#v?vNm7u!l+#BiQ1v-e;6M#ThyxE4- z54jez?J=_>#U}^|%!?%nfX>Y}N)gE+87D z-)4kKG~`W7aF~yjM;N4j3D?Y92})cL0x?sBfSJDUX*6!L>pp6{I;I>wER;mUq5kPr zIGn3&M}4;B+aL&-$iJ!3uWnc^?4Y(OL%B5gm#T0VN*+|Jm+RLqmzS*UnV7egf%tcE zaP}}Q#6%ApAj)yxHE#C*z!_E%_92(a23?RjkSrHo|82GdxzOk0UKvCW5YfYm9oCT} zk-~08PKIv1AB_e;IH|ca+mml1*aSA(wla1f*1E!s#;}b?k|D$-+L8iOn7@D`E&5z^yM1D7&>w1 z_aJgGzMIuis%BEt-!Q0o_~LVwKfPGKK~s&R4NV9Ln8Ctz%3%^?lOi zGnma@1~DS3%4FM3G0UABU>BCFaPXrF*uDEUsI1#s8Rtz*2Y+%vpdnz=o(s0J{LWs{ zuZ)Jcdey@ZbYQaikJ}2K#83O36^MWtiD)<0#7PHiGsv_vDz1=RdufmfLbXN#j)=l@ z@D=+?uxG3GL8kg0|J>Hhp-z5U{!xPx57QkC_p;Hj2}?oaE<^3+`D#in{fjovEvNQc z9UP%`Rj9q?-W+d|H}9Y~7Uh)L#Wky_b-Rl7OF)Pg8_aM;2Z!gG6|yw#PTFwJ^`2(B z<#dgbt5d#RB+U9Q<)){M5?sq_B8g~%Ih0?&dYy&t5###hb09}!`+tamrsoQW;TkFm6IN*f~X(L14u9$fbwQE>G;)IM@t#1zQ$iYqV_-toA2Z zxFHvFf@f7O^$6K6SkJBTIm=nbPEkr!Ttmr@$u-RPm>Oh9&V|3*&*WJqq}j9fTvOSD z^#+KGXg*p;2h*5o3ICo7ewqam?Z3@P7prSnjm# z5iRBz%B33qywXQp;|x70xrY++icug2ou3F`X$ccgw#zEtwp+ZqHrunEW2w(B2A@kT zvJyY{&;rl;#nc_Z9?XiLCwk(CKH2C>7bLN?h|Mo27L$tBbv&bK+Lb#LRT#=Vz*vLw z23uK{-VlT(&X~6-Flg&ud=JiQHB;k5*da zCQ?nPvD^zxMHU8&LPM%z&Z^CQ;FS@4&>PLcHYF@#ckVWhsrpa}*zSFHifGCULy+(` zUFWujfLpVl4=cd!?+ad#)f>li@rnV``qK;l7#14T%Ly&o`iEQY0Qe6pfGG zpU%b13o&R8lLkKR)(qUnw|+r=RqxC-D9w82Z|3R12AXH(Ww&WGcTEccMcSl>S+UF4 z6+;jkr6aEs6k1&jqq$Blrw=!3w>EYZC-7F=2Q$pGt!@_JL4pLR)SMJ~y5bc+9eGXH z`6fH=jOgM>MZi8*V%cS%DCf<)X!V$D;R>JM%F-0Zwz7bkdJyMK&9v-5uh(n)H9-H0 z;`8=MBJM(O2{I~i?R-l*zSn80=}rD%_Gk-MGjb4?qa-1h?&vSXj7NWqUMoWYvHDP; zYO8Zb4=__+iNsLzq?UMO;r49lHN#w|8A4e(sZoX)kRcgg#TFQSjd4bCjtH^uB&s~6 zJlG*$aE>>p2~3UUF28^RrGrg6Q(SQLaH3VAs2O{89-i|&cq;m5{Z$K(OaS=muom3& zYKgBSDp_?OF5vSnY;DgCqw;gSH8VMIH%qki+FhY39kL9x~j0FXVcXD}@(V>?QgJFoQPb`&R9qHfeo3)e{;j3AJ z$3kHAm9n@=&3vRU_Q~~t^2AburB)XFw+jAwt0r0g!d83tI}N9MVV%g+*b}pt2F&I6 zMS-tpioacEWL^(Ef155+ypYn-dj4knC`4b50?%NsgG>k$Ff&<6;Puw2ISn6g(~3S7 z%hE#u2&rKp=HOoVzpeKI55SFzQsV?6xna`XCdc4B8MWQLQAOqvKHnC1G1aF$k85T_ zIW_C~a-H3Y0~+6u8sEtx6>}7|nlA0^8YcoKrc#ri7im_?0%>!d*o`$t1I1$76z|X3 z`r7y!rMVG(rKKgJ!5M$K}9+2SnD}S(iiQugS-)#7O>%+kSf-b%nKAQ)@Om7Hh zgrmmDK9O^euCm|&8OgVhT9}$s!b52)#~daf&q;+vzd zdCe^K<>WfKn5k>K&2$*pofW7pCtK}8TT&YCbZ<$a8&GCw8)qpNo7?A)`K7xzVtgvK0e%efkkmY=~um(B;a4lxv=o|_>&sV1+_mzXVH4e z&hnE}oL~P`%>|#hpp#G9VEt(&?odhaJmsHC&2pA!^36}kR>6~Ce`=Z32C<3xo;aB| z|8hJvDDuW)#0eSfKT-YnLyoI@QUULO$pbua_vbUQf5mnzqvI~vU$Gs7q2t5Qf3@w{ z)Q%t3`fH@ena1&RX@7m&v131e2=8x5IIe3QKXLdslKxB@jt_181t7=O|G&5m03T~T zfrbA+0FY5M_zXE3eJ@f2006R-qldB=`*;WeZUW4l6_$uZK#ATk6>~gBE@}vQni1g+ zK%kg7B#b1d!|I}%FZ}wB7{$i{7#NTK>&I``pissu1(O$lQ80p!kpH+B{K+HG9l-I+ zw8Q;Lj)Mb0evu*=FE9ev9?oliXZ8JOC<6FJN`ixn_{6=<^!*)0`gA`oEF5|F$pcjLn~ou~f`aV}ab literal 0 HcmV?d00001 diff --git a/scripts/mac-catalyst/screenshots/KmpCompatList.png b/scripts/mac-catalyst/screenshots/KmpCompatList.png new file mode 100644 index 0000000000000000000000000000000000000000..333fca385e49b056cf2b642d38aec56fa865805e GIT binary patch literal 51161 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 Xp1r!{sNP3sV94$A4lQ zE-E)P)ih7eJ0?c+pNYcKsPOFy#}kLlFNX3r$HfGuB2rJzi?rhkUkBE9YmX+u=)+)b zbGdsJg+*atexur~JhkvUa^Dnzc2FWu42TV6~}J22vK+_KsnvA zW{-Wga9Tg7Ttd*%vfqR9ZrNhF=19h~iicM~i;83{C#A?b8G8`XSRMb5F9c~PfDKV* zz_GptC+U8(Idto!hhcuOf5Mg;ysk1*ByIBEf!%`}r#{fP9B@u?V~5TEf<#h9>3aFf zCoQrr|NGpd>;ddIHxfaok_qax&=N2zU30i}kY=E^3V*j#un}w^TZJ-fSa^FK^>S-f zDDeEzK`_fxT&%vExXqdcF){ewE?K;ARV`tCY-ci7Yqsj}Fo$H8XMd)d#VB}hn2{p= zPMrRGy?M}QooTzvdhMds8uCHD^=$q$>2T1=Bmh@f!{gSb--sN0zS1UiT9C-E>{LLy zO3cn!@1(k)2P#`(^kwa>8j~*8Basc~y_x3A7T|)@jwbX%e*uxe#w@3H?#AI)as;W^ z+6%0fiK}7(fhQZS&-R0>z_4DTXm)?jzrq-b4y}NDe$S)VG6y4YtND5w(1p50y0)5Q zhE z+V=bB%0u$8msUqVF)F~aGyhP-+Z_4yA#c8Xf6EmCqtcjai-&q@psQZQ7@62PVLA}9 z`CtImP(w%mpH8|m>_(RZF%eJlNQ^=Zu=pfQ4=?eIq;UoUFe=Q-lS9$`(VY>g{x|2i zd1I<$z9W$8?=9!cg=h=z?BtZ|sZzRc#$4jK_p$M3P`CSLzkhyT?of=BMNM9!#|_&_ zYwS5-ICh87cWM?gw(x@Sx`O)GlKUPmiwy_&7ktjurxzQ1deq#XX6jlZ;k|T$D8*-3 zt?Ypcxqy)RwanX7ZKj8E_^-N{8snuOS~fqM=E0@=`LXs(VE(?`T+Zd#Ss~c$cqwZp>m2`e<0^)`1n_CBRVH@#>;sI%=O+P}i;Q(Gc z5U|wfnS=f0ryv>601bp;74XfoPY!Yb^M2d3)GTRy%l55(kOkx!rrc}r2(jGCsKqpn zcN>T;vjTXF9{h>bx1}h9eI>R%#aL*$HQ$6Zw2a|%K8s4pdS|^R0|--Vpz`%T9i&l^ zh8(^9+q~oU`$*usnZdW-W(<;{4+j))IXg4a?sxWUoa$a(#0}V87V%ZsUHVlO54|t$ z?LL=$b_=HvauXANy}@11vS7VTgDR4}?TpJgbc<^H1!zZ+|K8pUt!S2EcLsDbR@_3& z;S1BoMe@Jed;(CfI~`#rqHM8Xv@Z@lrCTKqjDQar_BX>i02;l~45k)d?1#nqcdPC8 z=Y@UllV*=Resdgp+x>^k>1i4(KwJB7XuPJ@0ez4&RSt43kL>yj#xK{_b|SOS+wO&! z+x2*_oBg%?`{|tzd<-OitoGU&_}nby>3mpaQ7Y?b!8g`7N3y}xuxl$_P^o5TSb^F?>&_21PgZv18CPvj5biTf@*hy}YJPM>OTQ4!QB_*C zH9p)KsvI8NS^v@sPGdu9IK6S}(E!Cb`Du^5XFWF16KnJ7A@``c)P-H!y^fA&8b}^z zRgGTPJKtiSnDe6o8P7o~+9F_wc^ajq(BZPu`K0Glal~NiqR91!3(x2EM_UTPQFrnV z53x{5M76Je;)$yi?jfXDmD0&JPt?46t|=r=QBHBYj{|diyVhm;W!Kg3Gc8&IuAyFE zh@)+Ka(L}-?F&mMoETJO{&C;vRomPLtzhqCWW0SHv>ecvb=Zlc;B{B1&^SiCK#`m@b zU#Z2a0x`0WXfN-M`&?3+?Cq)KTr61uXX-v7d4+R#=pE})>9TFNjWRPFBxszh_K2Y0 zo+Afn7`b#m_I}c5^oaZ=dql;))8#m3V|*aX@;cR|Xsx3Cf>g(TI3HpNvoSR;G^pir zU)D9mG*JTGeK>x7|C<8KPw(Ab{YvMR=4$Tne1YAk9XqITf|}EXI*{i0k!qfdq?bci zS-`o>l>Mg(s^xYf%Z5?^7` zJ`t(VuHL?(Ka%2X0j@Mlk!R|La9)Ve2*B@^I$Yk~YAn?1&C!D|*WX#e!?eej+fR3i zI9l)|-?Hn=GHqXS|5O#9c=3r(S~#yp&Q{FXNeQyYPQ-@f-^aoAu_;OX>i1FW^L7p; z%~TfQ2EDlhiWH<4j3bk=&5j^~yuqZVZ&`0xov@@9&D`52e)S=}e=D zr@4fdkx7cOEPb}&(pCgtgjuG%J{B|(`)nROnTlLm;bq4cvi_R3&29T=&g4at-FFY$9mwmuR=hb=6FSh+XNFRR?r7^~`41Gc#AEt59WV1&py$8}_E`N>h$n?*71XS7h3)-Nv2caehMONczn~%`a40 zI$3qh#54R|S?&f0FDzGBB%DZ-t_rNN?--Uixu27B3oEr5Qr%a*)7pqb@4rjLF1&_k z(Y{SiFP`9Z?eVW)(h{3RYNF}oNhZh?&MhJUj2RzXbSRVO3wPu;EYJ(dVzHki$>1-# zDmD8iiKSY_y*-lo;$shnSn+X(N8I65lq9>beP$!U02783yL`yIH(3reOV8hadhJwT zA`vh*{9(0DM&Xw{h^P(3?7nXIqCWbxF%k(SNC%Mf>?_cLYuoSD8 z?3iiq_t5lZ7{%(fhRr$#K=q|qY;dxD#tmlBeKcK2kVhEqOK|wFTzDHx$mJ7t9a_G7 zm66aI5H~C<9mAKAyAsQM-@{R7Daj~VZa?^q=Es4-()mUu!AE|$*Zli(OpL@Wh{&YJ zRnOpd@Rzr$MYqNZwfsocaBQa(4!kG)r266d<4cgBz>a5zpWNs}1BLiwVu_?){9-q7 zkU$-h67Rt0m+tyZcaD#Hokri3+&CI!0QDy@XCd_oBmP8mSZennEU`Xr9j!|U3~1OI z)!=f-&-~2v3V+~v+wqkRVyyXaYe(STXqvpMq>*KPirX~IX-8`%lI0f^;!7R{W-AC} zMC&e%9XJ~?102uhUs{x2e)*a&yi@>R(tlce?dQbL#snztem*&nK%+&hz29o39w#&i zD_0pMPegfp?3j{2hqfU^!xE4hKEyz`5e*RYjMYbAr=t~!hAB`o-2k(0L*?!icg1C| zKwN0{<$f}Y9`k67@qa(2(1MUjqOWx#~?AM+G(d3R&_hf*o zhH+PpTpd$8&{-$PND;SHDdY@eV!;Cl_VSHA&~6tNJP|xUQ|?%J3=sj;#Y@kl;*&*^ zBoSmWm*@m7-RzY=8pnf<;5RXE!5c5Mg#gkcLLuA)=17{i^>`HR0S^zx6WK33H5skr zCjegl(#LCcgXZo#EeU9?AN_cL|4@BBQg5JiUuv)oUah zI(hJ%(rhlJJ^{_~j4Z-82&X-Yo#!HW zqn5kYDK@i1}U0C@+VUH#>GLie(lY!6p% zy?>ZKb(KinNQk^ma=?TP_<(*L*S#j+qik+c;oBAIAZonwjOM#;o>4KI=LhoigtWUa zuh|WlT0Y4)N@HvBqW884fl&0MG-iCcT6Xp4Vk7QLB&S*CYxY3ChcTe)mT z3PMJ%m{Y`{l0O7l5!3$oWbw+gr0%(+xxLJhn*2gCrG>jkYn?ED3%lZFl^+5T z=WcdLH3`7WvwSkGM1Rhf>d=e^Vs&xD z9xLAb)6diW;I+Ls_;nYdk@YejAL1*Gd}Z|VSHnY@Gl1~tp&FO4nj`IEk$8>%+NO0Y zHa@yk3ts*^Qqlp!DNYhi!~V`VD?l^rz_+FwpRQo>Ra^FcKvPh zPol$cE4-2Sz}oxy>7u@m8}#4U>29FkHPPI&OjkqPR`8#~Sy1U^AZFeU8OzZlof+R$OohQrbnvi3KVRG zZDY6*wI^SPrJpsil&!wT>J33IK)a8O*X2&8LZ4pT%c$Z>4B4+E%6-(_{VIXJ>3tOQAi_sAgph zaXJ5K>B{YsxeWsc77T$3cpq7Sa*{Xf?QZsm>#doNBPj3Q7miV0n^P3|H#f(Z*{yYL zEf4gA7w%g7{D99{_h(3*&LMTyNTM4eK#t+3bkK43CnPoHGTGPz1L?O!-ONKC7vjzl z)Oc(?*|VM^VI!+IzHA&|mv_G~M!P~f$m zHA$$p-VVmI=Q|8s4i~D|1l^89qSVIQPYj$*k_dbi>5Z`m>lo!0JaE1}9JU~Y+xhW& zBpBg-+}3AAN-0fq-ObdiHk5k-^6H^#v`JrY_5KDmPm*3l3aTDI`087}zEAq`S??ad z<T^XH_PjGV#miqK^ko^P;4UIDVyJyIt@lIjeY1s4OJyZ*q6 zyopma#zOFY$*eU$RYwCKB&GWe9%1#2Vqxk zybC2ThqfRX>*~+`j9YjnCcDCQ^FiUItNnXlxQB|o;Uh!%1H}t_6G>hQr;i5&|2+fH zo>b8&24@=ae7yiow&>^Uf#%2J=~b9~3HNI_++h${3XYKO3ivOO=l*Jla_MY`Q6tsO z%cEO=OFiNphiYg;axBZ*rP>Y-IJx^_C;D3atUhwIoZT)7)-Brmgofi@{ zq~oB0(B&U0vtQWwAf+~%<+ynQJR$fGb&o@__d6T?*4=C!OFhe*;9P7?EcZ43VE5IP zbhL7PZ6$=zYn4vHxX8;J90lfasL2m5&<^o+*&bf`q>Yf zqL%mN%39uQYc;0Dk9#@qUV1R}G3d&3ZFtB=zBcC2mHBb^C+{@kyI^!kGyTC+{M7XK zaU5G(kNyfni4C3XBlMg7mf5y@dc_VQ&o!rMTa6N;7WWUo#|O+ykbEW{U`oM{D;jcT2p_cNhi@#k3g>2~|$BI~0wHeAs(CkLNqDWqz|{ z@dlzPH``UK_sMXfyVxeh1*gSyrubf4cl*Wlsi)0&zL``R-D^}Uu|$5w+@jjxgYu|I zbc?OfdyidpDQy zafK%p%awm0Kg32n68{35o70?o?j30DR^jm+y-yYQ7eial=fB?>F5MjafntBQ5R3Cx zHu8T2Hf@Q112(Hc_0my=#Uc9Km)l|V*AU&wx7ZWx)yc&k)eEB=IW?{sSGq+O-9d=^ z4}Ag+^_R`)rzPWUh*)WM_=w!k^~U%EXXv=M>KoxwQ~s)moe%G|PBXQAf1S3OiMS7`V( zUk#v+GL_#RYG)rw!=V>5-F|2~Wl%D%7}ly??;yps)8R3GQA^Guz0h8KIH>8OnOqdG z5kzAR-@9f9%AEB;tt+Ug)VQk^I@=m$VNzQMDkbf@-+O0H5Z&eOF`;-{l^a)@W*j(! zj*$PlrjLUQf7tOzIhy_A^IgxDrscP*tJRSqLHL3Mt5m9%V?^xO}*TdeSjhBtPYspWVj0*>pZV&Y_9CmeiaJ!h}}qF|FH3M zOIYw}{q%R8WIhF+A>3(Cx8@7ate)3Y6M*{9?{^ERG`|aW)jgZO@ZL472Gm7Appf`9 z4T@)&`V4kI9Ee!|tZ+L>(z-A1Mhf%oq(rlVrS^FK=CVvF_Z|(o9DhC1ldkh?A&%ocR{=@^Y)CM^Jzg zI>D-DL%bJK!-B`Ino6SMw@7X%;iJw(f5AZ2FCBt&U}n;65~LRaB)ha#$qIp2x<4q} zwZB4jlW23mH8hFudLD`-yoKl)2&T(;0jrp$8e1hpAVCSX6%jl*ET}e=WFy8pm*exPMT0 zm8l<|AR51t=y@$!^+B5(28=_OImGx!>!L3@E@;bsE09T(xGAc(Hs$T%Nte6Yl_JH& zFFdYZ<-jU=$hq50v6KXagR<=ZZH!5mY!|>#q1NH^v-8Mm=d=41Ug(Av5VMm7AgS@z z{R`nBHNum8Hplw7w`{t-&GQVf7moMq?i2;YS9xWq#=ySWX%%*-b?lra%3@h0S}BY& zJ1G{=QZN7P&Q4BE z`i}HVpyyJg7RL;AdiCs`ISuiOJ%KMd?SNZ&+Gt`X?!q1@rbWV0U7KGg)kinYQplf3 zc2BB;iN;6lpNH^r_kXV2AgSxXJgonq}Ce&NDYBck{?gE;~l-`iC{~^j+mXK}Ogkl~Iyo{z&Z9TP0Z0!7Ax2an08a zCZySkqn+>4Fs57w<_@&{=Vmfyn6Ehr-ZD#_66E21^tr|}+DNIc9cwJJ)tt(#FWzif~*7})X$koG=7@6+_}wjSY5 zZcWj?Z0hHQOu7Sf>m4G%Qk1eu5Kc7GeS*42QBkwx_Eu zqaNOT^ZBJ-)uYd)_XG6V+c+<(_>P~`W2=k9b5RX9iF-Cpm3lVaGDeZ8_gX+Du`wlJ z`D7v+sCUKAq5SdeIaP1kt9^H6hTPCl^yf1k&iLa29&R7Q{CBM;9}Fep#LYTu=4{HQi7s!~Wla=)r8A+t018P*^_3525hsgUCa}J83ECnQ zA@1SUoIr;l-7Gp2&w2yhjEoMh`w}fyG;wJIhJ~2|d5@hm-R^%;Nql8fe_nq8#%ywv zA7t%?%y#CXxO5Ldz2@Op}H*pazV#Kmi29U0K|HJpXUCU1f44 zZClMniue($m|OYtAt@|DBXk}PZYI!DqfpVX8UX!%le%YCme!5BIj2s2~@o>LcICQ z=7K2Z8m|ekhpB;br>~~5pJLQxWH!Ff-j2dQ>Mq^R?ky&v&4Iw^c+>*V92VW4xIVT` z2+UAVK-#TOm6z(QwD@RrG`wnt9@wD^rbHBBojTTtb(sV>Lxg$Ll#Dc2#W6d;NE#)d z8~|FoRPST@h*a{`I~pC6IK>ubwth}zm<)cPpqQT35wp6!aN5rgm07EM;1#&AaL5w= zt6U5%BKNe(bZW)JNI1u2Ly}Vt8)G@20E8_7qtsfguC${Q|It zEZ?}VeK_o-1ATE~T{kpgQyAM6@HJ}F>DiDuZB^x8zOG`vMC1-lXplD(fHEdGD7^ z9DFf&4nZf1JmD*R06LYX3aWa4mx6v6XWu~Q8kOYP#BPAao@u#rt?QfGm`AtS5Hda& z9T@*cEoYA*sf}KuCK#r)0>Y0w&CEA0JofG-SX0$3zkQcWhd)~VfqLyl*L>)&VrRP= zX)sHtlcTZif5I&pxel?Epq22=AN^G4R}3pVj*y_5lX#l{9%m+(RM1cpwhd@3N5&HY zpj3?%EQ?D}+g58f`Zs(DJ6r3HD%l-rzNOpotUuW5!X<&X9QQm{UgO(D%2@M7QB}EZ z#wdwbt?IQc6wiso$-2`zlJMJ3Vq?d~Tx2{s(A>&|*7MdLTq;hMim;Ktspo*mD z1y;*ZU@@-C{IA3(sw`3pavzO%scdN`4_ZmJRkGeJlxUz>h8(B-8CWz>Va&3j5+YW* zPyo2%aJ(0QpElzjc6HlRzO^PCz!dX(PVIuwizw8eV$kObyH1Ngi@+EFOzl1%O{Z$W zfTA(ME;T7i$k;?IHzw&cs5>oOZaDVLUi9 zvcr}81$q&0Y%aKW91Fa*J0f3vs)|h9$MfNe%Krqa&b~eQ8@o;*0iDbQsB@4VTC!?a zZ;z>ZYZ-6RDHd<6(sO?y4~(iw4| zY@AXOx@0$kG6>rX9siaDRx7TDm_`ilVb^PjABzF^Zd&WCYYqvGV1f-Q{IKqt*bFwd zObpPM0nk%h2d?8*=Z{UG3r{JwZSVbE%B;6LYxZ=pUt9dmaff}gH+N6-HUAm1?xDZ$ zFBZL-T^V(r%O!U7qOWflRLe6edpiDeTEP`jb9Z*88B0Z)2j*;mo6BH&7PK4v*@Lq( zmD{mWj*!}0qoJ#GHLjE6pu&A`RDmc#Fd%OlT!uuqH#5ce3nvDuU>n~CX9sa9~u{QOY_Rty;w z+dP>Po%gsC9wYg{qj%SV=YGMZ`z&UY^ja3Rpb9>ChVRaHUBh8$PnLd$cF^;uAK2bd zYNA9AYJEnE;Om#tz_dY4v{7WD^vnwK8ku%)dsr%n<~1dyF>W|gW_RznpYVvmM3J@dt)MF|}vUvON5>RQ5|CR(amY378fNt{&cXXb9xfvOMAf(mpIpf|5 z?@(ve!2=TvY+-8c(HX3!&Hq1?)M>GaNsMnaeZ^4Pjy_G`@?vDuo%;e7>tL}0hs8e6 zYG*}}(>U>t(sL7Azxp`h7-+6Rd4z|@#4k>3ot#>iaW>ay@Yyy)#dS&7CY;AxF1eC3 ztqAs(%6{c&j+T7Sl2!gw0cbrD;k3OLo64kG!a17F;$Sg}!KomjZ@h~-PmU$Ue^Y=~ zWrB8hWcErqUR+{R7~-?nkF0pz74-+Y*&9AD9NvM5O6^Kgn6;Q9Liht%Sl|`Dd0Zs% z03Tc)L^qUNcA`bZSW;9h0z6$W#0Z(nN+m$8e+0>AWd39s@=zAOuB^!oO|(ga1e2T^BAw@#CdNCUn3vtq2tV#lIs{aEkzT z(V=pmq-O|vf{`4(8e%vTn6OH;-YrdAN`+ZaOjB|9qSs7Ec(k|*oti%#>-@9QFLcE6 zc7STt-|0Z<7MTC30|g1=|E3Pqw6hw_!isN!hPwYvqGNMMm{Gd)EXb-vWI|LD?8`sV zd?N~c;hF3ON?qjDb3lE6GMHn_dzPqP$A6iLN3Z>&$L^e`Jy;eQe%WB9w{7rgwKFu! zxad}Ha0W)=s+@`cdBRJ?KX3>eL2k;x7PVG(icP8AT8@)zA2|uPP|qY0?!KTSqOL-* z0Db=K%^xbz;ZIiaCZwLA|B~Z%zuJpwRS2fq&|FY=8O);n9Gg@FxtM>3$s-hO#1Nk& zXaC(A@{A<@R*H-Q=MZNZ{S*RpvYbWt{jPh*J^9$4IY7^5;p4k*(+jBMf$V;n2p7oc z+lw^+*;ZWvm-Rf5zBMO7Jh}KfI`SGFj|CDDgfAIQe4azpd~a{9S5TTI2Qp^n7Fm9V zco$4}IIsCWc%A7;{<+;zonTmkLx@8Ah{xZYBQXTb=w9FhL-%LQ7vHFa@l{K3@2K&s zrySi>;?!Q=8g+c5qCW;}9TVY>Ip3}?hpSdbRgc_M=G*MP>NwNtAZ+zBK?slRJ?$NX ze7&kTMcAc@BGzagqnDQ_T1m{!jU>TRj}4~JRxT4=&o(K-=15Kr`X9=) z#sS6mwaO<3NUN!!bcYt^zRJm$1IgcmwYn=qRRMr6h0<}cJ|$2#)xKPbA3apt!TK?X z-Vi4jlT$q-IDhuxwooJhRy#k$@w1{Y85hBHRZ*9hj)@Fk2gQ09{kN9}*kj1to-&S} z=12dWTXYWTDK84`@7?|{F{*v)f551Qalgl?@lTh=nrH6ZyS8yBiB#ia4=I3)scQu? zzMA2u3Do&-ro{NT%@QeG_~p754Dr3_()5LXHA5sRlqt*X6Y&c{8fZ|O_oC`^V)JvB zeJP`X|C~W{#B$>=v|757q$oXrZJIua>z8%H`hV)D*4e#yoLs)fps^ken z&W!9=>B5_~i7tNYlz$%2+Ivy3<9u*6N zWlXod&I1z46dbCDxDpp1p>%HB@5r-L%*HkW0#-}fjgp_2t9*Lc5K`LZ4@K-hZnz(v zt>nG;5zljm@he!s&LvK3Y)$Dd$di}s9`yN^&PBx2n+d$!!ml9_w@*>opL$}*l5L_# zSCJ|yM@+sVC>@v&f56N6Ukg9C^WPTjJDkr^uW{JU66t4j7MSMo=6vnm%`9*23GVTs zKhrWNWlie#k^HJGFJB#RiV-`r0A9N!9+&B@39C?c&ozMyv;&&+1Q6fxqjNWvi?U+& z37h7dc}YWx3~-t@0fC7mYv31eH1ZripWo(`uI!gM(-MmazUbzh`WdppxjPSceETUR z<6_b+2eWWJ)5|OvRm5k%dBpv^ezDylFu5q{7c83s;t!{9u}EBL=k?SRgq)dG;YBRK z8UH2wME%#Y&(NWZhZf||7m`?3aCSpk*eGM5K|`%rfumjRvD15hReC6LBT)g+c-#G| zA^+{9z%{qg{9V|~DXEf0xuQFRQA`|jAceeQGL-1T@XE94OJ>YIE5w(Xd0}$Qd%%s( zHN$ttJ&}5`N-gt_r*}ct-qJb=X@z6sx5K)pnZ}p@A^luY`C6uy5r@bW&D{xe%A9Lwzhie zYj>-Gafwu-w3cft%sEtU+p6MZw`bQr8&^NQhyMP~;XeJyMV9A%4-P-8SBCr*AcgDW zAFDqvt^Z#AdB=|Qf2jTptC9G$Djs=GxFc0EDcYw|3=0Ojk>HOk0Bf`!?zd0FU*g8k zzK}7@Av!0fCK^%o*TsT?RX*(=SbR^1joMb&=I);f{Vfc$(*2Llqp>jVYoIZDt5 zZY8Hp#%i)ItD#d~Q^30l30RL_Ut`rm6K=de#cL05vYMvB)jKQ%fEzC%*kC6amIIc| zqa_&6aqqVN4O2G!Mz@nV9y3RHIsk$pzaexMalaN`oQ9!hdjG5k|JTlhLKmn|SPVKH z6r=v9z5bR|s0^@GkfbDL(!ddlA2Y@s=%1vw-)`2Sg*8CGXwI~(lImi-1TI5-yD$NE z@YnanJ5F~V09CK&SxL=!2vVg0y@Rg@7@7{_ldrROpAisF8lelX0Zg`&tkC&xSh7

K>P@74gR;*)L54zkNInC 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 O{OD-vAIm#x74{!yKxO3s literal 0 HcmV?d00001 diff --git a/scripts/mac-catalyst/screenshots/KmpCompatState.png b/scripts/mac-catalyst/screenshots/KmpCompatState.png new file mode 100644 index 0000000000000000000000000000000000000000..92ef72b74ec207a3b67f4d4c5245d8d2f5ef02fc GIT binary patch literal 21449 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 VD7w8a4<;{xTXM>>dDje{{SS049^?Q3 literal 0 HcmV?d00001 diff --git a/scripts/mac-catalyst/screenshots/KmpCompatTypography.png b/scripts/mac-catalyst/screenshots/KmpCompatTypography.png new file mode 100644 index 0000000000000000000000000000000000000000..226fe6f990bbc16436863136636b7c62c3dc74cd GIT binary patch literal 30051 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 RM8d$IbEkDrWt_0~`(Fh2w_X4M literal 0 HcmV?d00001 diff --git a/scripts/macos/screenshots/KmpCompatState.png b/scripts/macos/screenshots/KmpCompatState.png new file mode 100644 index 0000000000000000000000000000000000000000..3339a22597f09ffd36d385cb2f78bb50d48c837d GIT binary patch literal 26536 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 literal 0 HcmV?d00001 diff --git a/scripts/macos/screenshots/KmpCompatTypography.png b/scripts/macos/screenshots/KmpCompatTypography.png new file mode 100644 index 0000000000000000000000000000000000000000..981fcfd42f1337213885a799eeb25391d59ecaba GIT binary patch literal 35324 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

~BmbW{_`>z) zS8v?sMgQwpHndzc&#?o@zgRR*B{BN@G+($7om&4pi~o1e81jc>MrMHj8lC7111ig` zBvug_{ZB@YQ!~$#;XX||Sk~q*yUSY(oS;(tpsp(qB=*>A; z>03g#^M8%@C0+T>hI`jB1H^Uow@x*n+1}1(vuh6*P6YX*Q626m2T=CrRMS9eYHF4C zH$&TVeJA9W=avJFK4ByQ%O-b=X^2xIUHu!KRXv*N~nzZuab|Kff>$cAtTn5j` z>cZBl;lT*^%ExsDO>Wau0BV1qi5Qp)dbb@OGJMR;d|Eg8nqS^yf|@7~ zfO7eV%t%t-hnwT(F6U{-vOt2yoNeIYu;&)T_3iE13!miVZh3A_0Ufg;UV!Od!z|Aa zRG(y8q&skqN^DC%cP(aEXY@Pp-H?ONE& zo_kr!zLrWI4~_2K_CnsK|2>6l58C^KYU@o3dt>fubK_Rvz=3n*ez*_2&Urxae0Wb1 zYFdDrb1MsPqe;uMu~$0B-NudsqBoBBNHKfAZn}N?Lzkt?1@m8KJ(Yp%H`XewsEQ)_pH*D5AomVuDIVL6U`3CfZx~ zI6R-mHdn)scPka1@A5n#Aa{9iv?Fz9dTqc?8ArC0NCq+^yO6OW8L~MAND+3K@*P(9 ziAD=Qr8+@*`bD3P!;di1Pza$@vBTS<9eXk$3x6t-BFGC^mVcQ4hWN;MpaXC~If&Od zeEG9spr+;8#?yn^vpEsp*){kAO|>;vSG-x@Fc>Z*8KB zpL8Qq%AztRPfc1{nlSu_;TKR3jTsggx!ky6M7Z0@D8klogvU-(DMg9;DtlMCuHmo~ ze`-Y~Q|58)$Ze4>L&aH60I@l60LDd=1tW#szs44d7*Z-sv>PXCk8MXr%ECN>7-XG2 zYzWfift(V`ZJVuzFFb)jDH+W2JZ@q3|HhkPL%h`% zq<#4t6$j2h0=pyr%5)<-`?-{kBS_qScUgY3h6;A{QaqF*uP%(8Cs{W*3lW%jjw_2w z`I5_PHejsXSeEsLwyS(!)I$7SKN}IzZflW%P5kXBqpRUuae^B&tpl0Q758%JEVNCv zG(7C1dw2((@{r*lU?GIK9NM}8=cBKYnJ$`(#~+Hy%v((la&mHnJNkxha<&O;{Fn2; zME|3mQB̃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 literal 0 HcmV?d00001 diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java index e9c92f92472..45c949402c2 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java @@ -1136,17 +1136,8 @@ static Path translateHelloSuiteDist() throws Exception { org.junit.jupiter.api.Assumptions.assumeFalse(configs.isEmpty(), "No JDK available to translate with"); CompilerHelper.CompilerConfig config = configs.get(0); - // Stage the Kotlin stdlib (the app is Kotlin) into a translator source root. - Path kotlinDir = Files.createTempDirectory("winhello-kotlin"); - Path m2 = Paths.get(System.getProperty("user.home"), ".m2", "repository", "org", "jetbrains", "kotlin"); - for (String[] ga : new String[][] { - { "kotlin-stdlib", "1.6.0" }, { "kotlin-stdlib-jdk7", "1.6.0" }, { "kotlin-stdlib-jdk8", "1.6.0" }, - { "kotlin-stdlib-common", "1.6.0" }, { "kotlin-annotations-jvm", "1.6.0" } }) { - Path jar = m2.resolve(ga[0]).resolve(ga[1]).resolve(ga[0] + "-" + ga[1] + ".jar"); - if (Files.exists(jar)) { - extractJar(jar, kotlinDir); - } - } + // Kotlin's runtime comes with the application's other libraries, in + // libraryClasses (translation-libs), at the version it was compiled against. Parser.cleanup(); Path sourceDir = Files.createTempDirectory("winhello-sources"); @@ -1156,7 +1147,7 @@ static Path translateHelloSuiteDist() throws Exception { CompilerHelper.compileJavaAPI(javaApiDir, config); String cp = coreClasses + java.io.File.pathSeparator + portClasses - + java.io.File.pathSeparator + commonClasses + java.io.File.pathSeparator + kotlinDir + + java.io.File.pathSeparator + commonClasses + java.io.File.pathSeparator + libraryClasses; List appCompile = new java.util.ArrayList<>(Arrays.asList( "-source", config.targetVersion, "-target", config.targetVersion, @@ -1176,7 +1167,7 @@ static Path translateHelloSuiteDist() throws Exception { } Path outputDir = Files.createTempDirectory("winhello-out"); - String sources = classesDir + ";" + commonClasses + ";" + kotlinDir + ";" + coreClasses + ";" + String sources = classesDir + ";" + commonClasses + ";" + coreClasses + ";" + portClasses + ";" + javaApiDir + ";" + nativeStage + ";" + libraryClasses; // The "windows" app type binds CodenameOneImplementation to its @Concrete // win() target (WindowsImplementation) during translation -- no override. diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetLinuxIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetLinuxIntegrationTest.java index 2a0596b2bb8..f28c504b23e 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetLinuxIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetLinuxIntegrationTest.java @@ -214,17 +214,8 @@ static Path translateHelloSuiteDistLinux(String launcherSource) throws Exception org.junit.jupiter.api.Assumptions.assumeFalse(configs.isEmpty(), "No JDK available to translate with"); CompilerHelper.CompilerConfig config = configs.get(0); - // Stage the Kotlin stdlib (the app is Kotlin) into a translator source root. - Path kotlinDir = Files.createTempDirectory("linuxhello-kotlin"); - Path m2 = Paths.get(System.getProperty("user.home"), ".m2", "repository", "org", "jetbrains", "kotlin"); - for (String[] ga : new String[][] { - { "kotlin-stdlib", "1.6.0" }, { "kotlin-stdlib-jdk7", "1.6.0" }, { "kotlin-stdlib-jdk8", "1.6.0" }, - { "kotlin-stdlib-common", "1.6.0" }, { "kotlin-annotations-jvm", "1.6.0" } }) { - Path jar = m2.resolve(ga[0]).resolve(ga[1]).resolve(ga[0] + "-" + ga[1] + ".jar"); - if (Files.exists(jar)) { - CleanTargetIntegrationTest.extractJar(jar, kotlinDir); - } - } + // Kotlin's runtime comes with the application's other libraries, in + // libraryClasses (translation-libs), at the version it was compiled against. Parser.cleanup(); Path sourceDir = Files.createTempDirectory("linuxhello-sources"); @@ -234,7 +225,7 @@ static Path translateHelloSuiteDistLinux(String launcherSource) throws Exception CompilerHelper.compileJavaAPI(javaApiDir, config); String cp = coreClasses + java.io.File.pathSeparator + portClasses - + java.io.File.pathSeparator + commonClasses + java.io.File.pathSeparator + kotlinDir + + java.io.File.pathSeparator + commonClasses + java.io.File.pathSeparator + libraryClasses; List appCompile = new ArrayList<>(Arrays.asList( "-encoding", "UTF-8", @@ -264,7 +255,7 @@ static Path translateHelloSuiteDistLinux(String launcherSource) throws Exception } Path outputDir = Files.createTempDirectory("linuxhello-out"); - String sources = classesDir + ";" + commonClasses + ";" + kotlinDir + ";" + coreClasses + ";" + String sources = classesDir + ";" + commonClasses + ";" + coreClasses + ";" + portClasses + ";" + javaApiDir + ";" + nativeStage + ";" + libraryClasses + ";" + resStage; CleanTargetIntegrationTest.runTranslatorMultiSource(sources, outputDir, "LinuxHelloMain", "linux"); From e1a2ea2d0fde0379b9e9a48ad87c95142bf6a140 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Oct 2026 03:57:34 +0200 Subject: [PATCH 06/32] ci: guide workflows watch the KMP snippet tree; Android keeps the app's coroutines version Co-Authored-By: Claude Opus 5.5 --- .github/workflows/check-doc-snippets.yml | 1 + .github/workflows/developer-guide-docs.yml | 4 ++- .github/workflows/website-docs.yml | 1 + .../builders/AndroidGradleBuilder.java | 26 +++++++++++++++++-- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-doc-snippets.yml b/.github/workflows/check-doc-snippets.yml index 1461f1ba69f..a5dbf44b4f3 100644 --- a/.github/workflows/check-doc-snippets.yml +++ b/.github/workflows/check-doc-snippets.yml @@ -16,6 +16,7 @@ on: - 'scripts/wayline/**' - 'scripts/savor/**' - 'scripts/skirmish/**' + - 'scripts/hellocodenameone/common/src/main/kmp/**' - 'docs/website/content/blog/**' - 'CodenameOne/src/**/*.java' - 'Ports/CLDC11/src/**/*.java' diff --git a/.github/workflows/developer-guide-docs.yml b/.github/workflows/developer-guide-docs.yml index 62d4911a31e..677f155e2e3 100644 --- a/.github/workflows/developer-guide-docs.yml +++ b/.github/workflows/developer-guide-docs.yml @@ -19,6 +19,7 @@ on: - 'scripts/savor/**' - 'scripts/skirmish/**' - 'scripts/parley/**' + - 'scripts/hellocodenameone/common/src/main/kmp/**' # The API-name check derives the published pages from the javadoc's own # sources, and the guide links into the java.* compatibility classes -- # java/util/List.html among them. A change that renames or deletes one @@ -130,12 +131,13 @@ jobs: - 'Ports/JavaSE/**' - 'maven/core/**' - 'maven/javase/**' - # The Wayline, Savor and Parley chapters include these trees by tag, so a + # The Wayline, Savor, Parley and Kotlin Multiplatform chapters include these trees by tag, so a # renamed or deleted tag there has to reach the snippet check. - 'scripts/wayline/**' - 'scripts/savor/**' - 'scripts/skirmish/**' - 'scripts/parley/**' + - 'scripts/hellocodenameone/common/src/main/kmp/**' workflow: - '.github/workflows/developer-guide-docs.yml' diff --git a/.github/workflows/website-docs.yml b/.github/workflows/website-docs.yml index 2f5096954b9..b0f56b54ccb 100644 --- a/.github/workflows/website-docs.yml +++ b/.github/workflows/website-docs.yml @@ -66,6 +66,7 @@ on: - 'scripts/savor/**' - 'scripts/skirmish/**' - 'scripts/parley/**' + - 'scripts/hellocodenameone/common/src/main/kmp/**' - 'scripts/website/**' - 'scripts/skindesigner/**' - 'Ports/JavaScriptPort/**' diff --git a/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index 3f1c8a9d647..6d71b5d8f2d 100644 --- a/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -1673,6 +1673,7 @@ public boolean build(File sourceZip, final BuildRequest request) throws BuildExc extendAppCompatActivity = request.getArg("android.extendAppCompatActivity", "false").equals("true"); // When using gradle 8 we need to strip kotlin files from user classes otherwise we get duplicate class errors stripKotlinFromUserClasses = useGradle8; + gradleProvidedLibraryDependencies = ""; if (!request.getArg("android.java8", "true").equals("true")) { // Java 6 source level was produced by running retrolambda over the compiled // classes. Retrolambda is gone (it never supported the JDK 17 that Gradle 8 @@ -12513,14 +12514,35 @@ private String stripGradleProvidedLibraries(File dummyClassesDir) { for (String[] lib : GRADLE_PROVIDED_LIBRARIES) { File directory = new File(dummyClassesDir, lib[0].replace('/', File.separatorChar)); if (directory.isDirectory()) { - log("Deleting directory " + directory + "; declaring " + lib[1] + " instead"); + String coordinate = lib[1]; + if (lib[0].equals("kotlinx/coroutines")) { + // The version the application shipped, so a newer one is never + // downgraded; the jar records it in this file. + String shipped = readTrimmed(new File(dummyClassesDir, + "META-INF" + File.separator + "kotlinx_coroutines_core.version")); + if (shipped != null && shipped.length() > 0) { + coordinate = coordinate.substring(0, coordinate.lastIndexOf(':') + 1) + shipped; + } + } + log("Deleting directory " + directory + "; declaring " + coordinate + " instead"); delTree(directory, true); - deps.append(" implementation '").append(lib[1]).append("'\n"); + deps.append(" implementation '").append(coordinate).append("'\n"); } } return deps.toString(); } + private static String readTrimmed(File f) { + if (!f.isFile()) { + return null; + } + try { + return new String(java.nio.file.Files.readAllBytes(f.toPath()), "UTF-8").trim(); + } catch (IOException ex) { + return null; + } + } + private void stripKotlin(File dummyClassesDir) { String[] skipDirectories = new String[] { "org" + File.separator + "jetbrains" + File.separator + "annotations", From f81fd998ab28c7c1be61deb561db57fe0ad066cd Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Oct 2026 06:58:45 +0200 Subject: [PATCH 07/32] ci: Android strips the coroutines jar's _COROUTINE frames too; guide names real animate functions Co-Authored-By: Claude Opus 5.5 --- .../Kotlin-Multiplatform-Interop.asciidoc | 4 ++-- .../com/codename1/builders/AndroidGradleBuilder.java | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc index 8019ca5d3ae..7c5c5398207 100644 --- a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -149,7 +149,7 @@ Codename One `Container`: in Compose. * **Frames come from the Codename One animation loop.** `ComposeHost` runs the `Recomposer` with a `BroadcastFrameClock` that `Form.registerAnimated` - ticks, so `withFrameNanos`, `animate*AsState` and `AnimatedVisibility` run + ticks, so `withFrameNanos`, `animateFloatAsState` and `AnimatedVisibility` run in step with every other Codename One animation, and an idle screen costs nothing. * **`Dispatchers.Main` is the EDT.** kotlinx.coroutines finds its main @@ -186,7 +186,7 @@ Material 3:: `MaterialTheme` and `ColorScheme`, `Scaffold`, `Surface`, `AssistChip`, `FilterChip`, `Checkbox`, `Switch`, the progress indicators, dividers, `Icon` with `Icons.Filled`, `Icons.Outlined` and `Icons.AutoMirrored`. -Animation:: `AnimatedVisibility`, `animate*AsState`, +Animation:: `AnimatedVisibility`, `animateFloatAsState`, `animateDpAsState`, `animateColorAsState`, `rememberInfiniteTransition` with `animateFloat`. Images:: `Image` with `ImageVector` and `ContentScale`. diff --git a/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index 6d1667ac34e..ee6563d2cc5 100644 --- a/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -12672,6 +12672,9 @@ private boolean hasSourceFileWithExtension(File dir, String extension) { // an older androidx.collection without the ScatterSet the Compose runtime calls. private static final String[][] GRADLE_PROVIDED_LIBRARIES = { {"kotlinx/coroutines", "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2"}, + // The coroutines jar's debugger frames, outside its package; no coordinate + // of its own, the line above already declares the jar. + {"_COROUTINE", null}, {"androidx/collection", "androidx.collection:collection:1.5.0"}, {"androidx/annotation", "androidx.annotation:annotation:1.9.1"} }; @@ -12693,8 +12696,12 @@ private String stripGradleProvidedLibraries(File dummyClassesDir) { coordinate = coordinate.substring(0, coordinate.lastIndexOf(':') + 1) + shipped; } } - log("Deleting directory " + directory + "; declaring " + coordinate + " instead"); delTree(directory, true); + if (coordinate == null) { + log("Deleted directory " + directory); + continue; + } + log("Deleted directory " + directory + "; declaring " + coordinate + " instead"); deps.append(" implementation '").append(coordinate).append("'\n"); } } From 9ac1593bd2dd9d29460a3a3387552431f39b615b Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Oct 2026 11:13:14 +0200 Subject: [PATCH 08/32] ParparVM on Windows opens long and non-ASCII paths; hello C build guard covers the Compose screens Co-Authored-By: Claude Opus 5.5 --- vm/ByteCodeTranslator/src/cn1_win_compat.c | 68 +++++++++++++++++++ vm/ByteCodeTranslator/src/cn1_win_compat.h | 11 +++ vm/ByteCodeTranslator/src/nativeMethods.m | 12 +++- .../CleanTargetIntegrationTest.java | 5 +- 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/vm/ByteCodeTranslator/src/cn1_win_compat.c b/vm/ByteCodeTranslator/src/cn1_win_compat.c index 77d57657a13..088f0736bcf 100644 --- a/vm/ByteCodeTranslator/src/cn1_win_compat.c +++ b/vm/ByteCodeTranslator/src/cn1_win_compat.c @@ -36,6 +36,9 @@ #include /* _beginthreadex */ #include #include +#include +#include +#include #include "cn1_win_compat.h" @@ -565,4 +568,69 @@ int cn1_win_zone_offset_millis(const char* zoneId, long long millis, int* offset return 1; } +FILE* cn1_win_fopen_utf8(const char* path, const char* mode) { + wchar_t wmode[8]; + wchar_t* wide; + wchar_t* full; + wchar_t* target; + DWORD fullLen; + int wideLen; + int i; + FILE* f; + for(i = 0; mode[i] != 0 && i < 7; i++) { + wmode[i] = (wchar_t)(unsigned char)mode[i]; + } + wmode[i] = 0; + wideLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, NULL, 0); + if(wideLen <= 0) { + errno = EINVAL; + return NULL; + } + wide = (wchar_t*)malloc(sizeof(wchar_t) * (size_t)wideLen); + if(wide == NULL) { + errno = ENOMEM; + return NULL; + } + MultiByteToWideChar(CP_UTF8, 0, path, -1, wide, wideLen); + /* Already prefixed, or short enough for the classic API: as given. */ + if(wideLen - 1 < MAX_PATH - 12 || wcsncmp(wide, L"\\\\?\\", 4) == 0) { + f = _wfopen(wide, wmode); + free(wide); + return f; + } + fullLen = GetFullPathNameW(wide, 0, NULL, NULL); + if(fullLen == 0) { + f = _wfopen(wide, wmode); + free(wide); + return f; + } + /* Room for \\?\ or \\?\UNC\ in front of the resolved path. */ + full = (wchar_t*)malloc(sizeof(wchar_t) * ((size_t)fullLen + 8)); + if(full == NULL) { + free(wide); + errno = ENOMEM; + return NULL; + } + target = full + 8; + if(GetFullPathNameW(wide, fullLen, target, NULL) == 0) { + f = _wfopen(wide, wmode); + free(full); + free(wide); + return f; + } + if(target[0] == L'\\' && target[1] == L'\\') { + /* \\server\share\x becomes \\?\UNC\server\share\x */ + target += 2; + target -= 8; + memcpy(target, L"\\\\?\\UNC\\", 8 * sizeof(wchar_t)); + } else { + target -= 4; + memcpy(target, L"\\\\?\\", 4 * sizeof(wchar_t)); + } + f = _wfopen(target, wmode); + free(full); + free(wide); + return f; +} + #endif /* _WIN32 */ diff --git a/vm/ByteCodeTranslator/src/cn1_win_compat.h b/vm/ByteCodeTranslator/src/cn1_win_compat.h index 4ada0eb600d..7ad7d1dc49e 100644 --- a/vm/ByteCodeTranslator/src/cn1_win_compat.h +++ b/vm/ByteCodeTranslator/src/cn1_win_compat.h @@ -45,6 +45,7 @@ #include /* struct timespec (C11), localtime_s, _mkgmtime */ #include #include /* _putenv_s */ +#include /* FILE, for cn1_win_fopen_utf8 */ #ifdef __cplusplus extern "C" { @@ -181,6 +182,16 @@ void cn1_win_resume_thread(void* handle); int cn1_win_zone_offset_millis(const char* zoneId, long long millis, int* offsetOut, int* dstOut, int* rawOut); +/* --- opening a file by a UTF-8 path --- + fopen on Windows reads its argument in the ANSI code page and stops at + MAX_PATH (260) characters, so a UTF-8 path with a non-ASCII name, or any + path longer than that, failed to open although the file was there. This + converts to UTF-16, resolves the path (which is also what removes "..", + since the long-path prefix turns normalization off), adds the \\?\ prefix + when the result is too long for the classic API, and calls _wfopen. mode + is an ASCII fopen mode. Returns NULL with errno set, as fopen does. */ +FILE* cn1_win_fopen_utf8(const char* path, const char* mode); + /* --- environment / time.h POSIX helpers absent from MSVC --- Thin static-inline wrappers over the MSVC equivalents; used by the date / timezone runtime in nativeMethods. */ diff --git a/vm/ByteCodeTranslator/src/nativeMethods.m b/vm/ByteCodeTranslator/src/nativeMethods.m index d57c7d5d8f4..62fe726b4a7 100644 --- a/vm/ByteCodeTranslator/src/nativeMethods.m +++ b/vm/ByteCodeTranslator/src/nativeMethods.m @@ -1275,7 +1275,11 @@ JAVA_LONG java_io_FileInputStream_openImpl___java_lang_String_R_long(CODENAME_ON valid across the safepoint. */ FILE* f; CN1_YIELD_THREAD; +#if defined(_WIN32) + f = cn1_win_fopen_utf8(path, "rb"); +#else f = fopen(path, "rb"); +#endif if(f != NULL) { /* Same reasoning, and the same 16KB ceiling for the same reason: it is * dirty memory held per open stream. */ @@ -1301,7 +1305,9 @@ JAVA_LONG java_io_FileInputStream_openImpl___java_lang_String_R_long(CODENAME_ON * * 2. PATHS ARE PASSED TO THE NARROW CRT. stringToUTF8 produces UTF-8, and the * Windows CRT's fopen reads it in the active ANSI code page, so a path holding - * a non-ASCII user or file name fails to open. The same mismatch runs through + * a non-ASCII user or file name fails to open. The two stream opens below no + * longer do (cn1_win_fopen_utf8, which also lifts MAX_PATH: the self-hosted + * translator failed on a 270-character Kotlin class path); the rest still do. The same mismatch runs through * java_io_File.m's stat/access/FindFirstFile calls. cn1_db_sqlite_impl.h around * line 196 already documents this exact problem and converts UTF-8 to UTF-16 * before calling the wide API; the file layer needs the same treatment applied @@ -1459,7 +1465,11 @@ JAVA_LONG java_io_FileOutputStream_openImpl___java_lang_String_boolean_R_long(CO /* The mirror of the read side: opening a FIFO for write waits for a reader. */ FILE* f; CN1_YIELD_THREAD; +#if defined(_WIN32) + f = cn1_win_fopen_utf8(path, append ? "ab" : "wb"); +#else f = fopen(path, append ? "ab" : "wb"); +#endif if(f != NULL) { /* stdio's default buffer is a few kilobytes, so writing a file of any * size becomes that many write(2) calls. A bigger one cuts them by the diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java index 45c949402c2..c1fa2210c75 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java @@ -2038,11 +2038,14 @@ static String runCmakeBuild(Path buildDir, Path workingDir) throws Exception { // four-core Linux runner a healthy build takes close to 30 minutes, and // one was stopped at 5,248 of 5,413 steps; it must not share the // ten-minute deadline used to detect a hung test executable either. + // The Compose screens added about 1,600 more (the Compose runtime and + // kotlinx.coroutines), and a healthy build then ran past 45 minutes at + // 7,024 of 7,025 steps. // This is a guard against a hang, not a performance budget: the perf // gate is what judges speed. ProcessBuilder builder = new ProcessBuilder("cmake", "--build", buildDir.toString()); builder.directory(workingDir.toFile()); - return runCommand(builder, 45, java.util.concurrent.TimeUnit.MINUTES); + return runCommand(builder, 80, java.util.concurrent.TimeUnit.MINUTES); } static String runCommand(List command, Path workingDir) throws Exception { From a911a2f8f925f26838a8edde566474ecdf3c6843 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Oct 2026 11:14:18 +0200 Subject: [PATCH 09/32] compose-compat drops the coroutines metadata-only root artifact; Windows KmpCompat goldens Co-Authored-By: Claude Opus 5.5 --- maven/compose-compat/pom.xml | 10 ++++++++++ scripts/windows/screenshots/KmpCompatLayout.png | Bin 0 -> 6774 bytes scripts/windows/screenshots/KmpCompatList.png | Bin 0 -> 20809 bytes scripts/windows/screenshots/KmpCompatState.png | Bin 0 -> 6649 bytes .../windows/screenshots/KmpCompatTypography.png | Bin 0 -> 9973 bytes 5 files changed, 10 insertions(+) create mode 100644 scripts/windows/screenshots/KmpCompatLayout.png create mode 100644 scripts/windows/screenshots/KmpCompatList.png create mode 100644 scripts/windows/screenshots/KmpCompatState.png create mode 100644 scripts/windows/screenshots/KmpCompatTypography.png diff --git a/maven/compose-compat/pom.xml b/maven/compose-compat/pom.xml index 3d811d26e17..67492625228 100644 --- a/maven/compose-compat/pom.xml +++ b/maven/compose-compat/pom.xml @@ -58,6 +58,16 @@ androidx.compose.runtime runtime-desktop ${compose.runtime.version} + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + dL65b3 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 I#>o4>0T@GqmH+?% literal 0 HcmV?d00001 From 6e41bfc3eb783913940a3560527ad1917bc704ea Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Oct 2026 19:57:16 +0200 Subject: [PATCH 13/32] Android raises a pinned Kotlin older than the shipped coroutines' stdlib; arm64 Linux suite gets swap; Android KmpCompat goldens Co-Authored-By: Claude Opus 5.5 --- .github/workflows/linux-build-run.yml | 14 ++++++++ .../builders/AndroidGradleBuilder.java | 31 ++++++++++++++---- .../android/screenshots/KmpCompatLayout.png | Bin 0 -> 15449 bytes scripts/android/screenshots/KmpCompatList.png | Bin 0 -> 26526 bytes .../android/screenshots/KmpCompatState.png | Bin 0 -> 16143 bytes .../screenshots/KmpCompatTypography.png | Bin 0 -> 22305 bytes 6 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 scripts/android/screenshots/KmpCompatLayout.png create mode 100644 scripts/android/screenshots/KmpCompatList.png create mode 100644 scripts/android/screenshots/KmpCompatState.png create mode 100644 scripts/android/screenshots/KmpCompatTypography.png diff --git a/.github/workflows/linux-build-run.yml b/.github/workflows/linux-build-run.yml index 620ae9c7e25..faf61d9f108 100644 --- a/.github/workflows/linux-build-run.yml +++ b/.github/workflows/linux-build-run.yml @@ -235,6 +235,20 @@ jobs: - name: Install clang and time run: bash scripts/ci/apt-get-install.sh clang time + # The suite's C build compiles ~7,000 translation units, several of them + # large, with full debug info. On ubuntu-24.04-arm it exhausted memory and + # the runner was shut down mid-build, twice at the same step; extra swap + # lets the peak spill instead of taking the machine down. + - name: Add swap (arm64) + if: matrix.arch == 'arm64' + run: | + free -m + sudo fallocate -l 12G /cn1-swapfile + sudo chmod 600 /cn1-swapfile + sudo mkswap /cn1-swapfile + sudo swapon /cn1-swapfile + free -m + - name: Set up JDK 8 uses: actions/setup-java@v5 with: { distribution: 'temurin', java-version: '8' } diff --git a/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index 72675ffd845..9625222d0fb 100644 --- a/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -8284,12 +8284,31 @@ && watchModuleName(request) != null && legacyGplayServicesMode) { } if (hasKotlinSources && kotlinVersion.length() == 0 && !builtInKotlin) { kotlinVersion = useGradle8 ? "1.9.22" : "1.7.22"; - // A declared kotlinx-coroutines brings its own kotlin-stdlib (2.1 for - // coroutines 1.10), and a 1.9 compiler refuses that stdlib's metadata - // ("compiled with an incompatible version of Kotlin"), so the default - // compiler follows it. A version the project pinned is left alone. - if (useGradle8 && gradleProvidedLibraryDependencies.contains("kotlinx-coroutines-core")) { - kotlinVersion = KOTLIN_FOR_GRADLE_PROVIDED_COROUTINES; + } + // A declared kotlinx-coroutines brings its own kotlin-stdlib (2.1 for coroutines + // 1.10), and an older compiler refuses that stdlib's metadata ("compiled with an + // incompatible version of Kotlin"), so the compiler is raised to read it. That + // includes a pinned version: the Health Connect floor above pins 1.9.x, and no + // lower pin can compile against the stdlib the application itself shipped. + if (hasKotlinSources && kotlinVersion.length() > 0 && useGradle8 + && gradleProvidedLibraryDependencies.contains("kotlinx-coroutines-core")) { + String comparable = HealthManifestFragments.numericVersionPrefix(kotlinVersion); + if (comparable == null + || compareVersions(comparable, KOTLIN_FOR_GRADLE_PROVIDED_COROUTINES) < 0) { + if (request.getArg("android.topDependency", "").contains("kotlin-gradle-plugin")) { + // The app's own kotlin-gradle-plugin line wins over ours, so only + // the app can raise the compiler; say so instead of claiming to. + log("WARNING: the application ships kotlinx-coroutines, whose kotlin-stdlib" + + " needs Kotlin " + KOTLIN_FOR_GRADLE_PROVIDED_COROUTINES + + " or newer, but android.topDependency pins its own" + + " kotlin-gradle-plugin; raise it if :app:compileDebugKotlin" + + " reports an incompatible Kotlin version"); + } else { + log("The application ships kotlinx-coroutines, whose kotlin-stdlib Kotlin " + + kotlinVersion + " cannot read; compiling with Kotlin " + + KOTLIN_FOR_GRADLE_PROVIDED_COROUTINES + " instead"); + kotlinVersion = KOTLIN_FOR_GRADLE_PROVIDED_COROUTINES; + } } } String kotlinPluginApply = ""; diff --git a/scripts/android/screenshots/KmpCompatLayout.png b/scripts/android/screenshots/KmpCompatLayout.png new file mode 100644 index 0000000000000000000000000000000000000000..8f5ab8bd0385706bbad276c7326aa6ccba67eda6 GIT binary patch literal 15449 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%0AEZ@~wUqa~i_9MmFPN+=ClG3)wrjLPu z89lSJ&GGs?;`-_p7^W|7LHsMCg^fUwA2+rK;tY%|V z|83KTF>FAtWn^T2cXqZ=eXdwY^R{DV7oj|qH46vB@et%ZT^+_J;?k7agmF8ABsLi1 z4KrYCs2($5&)}^E``6R;7#{feg~nyrVE6#MD`6cr8UC&M^(2N~z8FNaJsS;BVCK(yogO_6@u+yEEHGv#370)nL83=}ycS5t`@6d`mzusT8_&#vf zW?zEF(Kh7r6?fvQ!aHT7+Up}yxO|Dzh*blqL!oU0w`s0=T61m^p3gKJ@OZWUxiP$r->hPaHLB3s#-Ms)AwR5 zD5kvVeH$E%Hnmw}GoXfG*>b)I1fG?nO(+#mB3rj z@18%8%*>=VMvA~hon>oM|6-h!C=-|H~z?1 zNN>-b0b`5V|1zlvX+`cG3OMAPf0P7%#V^NMgKJotgjXCGY@Zil8NiuGr>3KOtHcx# z#n&)_O+{sQv@?xg;T-n20tRyox;qG7DyXXBI^WgFI*H9Ie1Tl}Tg_)Dn!@CR?FaJ~mR0!X+mAmR)vnoqX$kx^3GV^>tG>QT32AIF^ZZTkyUwiWe;7)= zJUxHALe^QCw4A~FDAeZiPgw#buVKltlfbjul=^jF?TcnJn6LeqgvssbC?+8$XP(&( zC_FQmt!+L=nT*MUc5xp)nIPA#(M3Oo2C>WDVX&S1? zYpIP9ulC%IVwA34>`8M46=FC4-1Xqc$mp>Ez#{s*8?k8cVhGd-#>Vz%QcwRkKZr8G z*}diQ$u%%VNIdwkRdY!pSF4*Q)!jR!(nP~E7=`Pawu2Q0MH#^e7a;;#G4K)wJ1VYG zBbH{p{r$J`uU2gq&JyRAwxR%4c`+@Szj4+Ebf@+ByWAuhFYGObt@woGuycG_#YoC- zx1BBDC~6rz5fKso0$rQ}gZns;)Q!I;PttDe_-=Pd#w=8{xp_fN&%iL>@Zyd|(6Ksj zSzEw4bsF#lr=>9%6q>}?yY#}r>m@^B4u!@Qv|z&YE{lteO=v})deiUu9Boi1!JJd` z7+eyb$nIhm0M@2_3+h|tnP<;cQPiIH&k@R)WkDou(0t&yV=7vPP6(tSDVtK2O F{{>hJLY4pk literal 0 HcmV?d00001 diff --git a/scripts/android/screenshots/KmpCompatList.png b/scripts/android/screenshots/KmpCompatList.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb8f3fe7c9ffa678ef3df0c8746aa0a08cd2b26 GIT binary patch literal 26526 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 LTji~iMbN(i(ssC+ literal 0 HcmV?d00001 diff --git a/scripts/android/screenshots/KmpCompatState.png b/scripts/android/screenshots/KmpCompatState.png new file mode 100644 index 0000000000000000000000000000000000000000..7f8d51399c181d8d1c22f5887eae04640f8ec3cd GIT binary patch literal 16143 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 BPHO-F literal 0 HcmV?d00001 From 8e72bbe3cae7c90c554c50200da199b236f0f49b Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 27 Oct 2026 01:15:19 +0200 Subject: [PATCH 14/32] ParparVM drops field-updater CAS bodies whose classes the dead-code pass removed An application that never reaches a lowered updater lost the owner class and its header, and the Linux demo build then failed on a missing coroutines header. Co-Authored-By: Claude Opus 5.5 --- .../codename1/tools/translator/Parser.java | 1 + .../tools/translator/RuntimeLowering.java | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java index b49770ac756..4b015e95b47 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java @@ -1672,6 +1672,7 @@ public static void writeOutput(File outputDirectory) throws Exception { verifyNativeMethods(handWrittenNativeSources); generateClassAndMethodIndexHeader(outputDirectory); + RuntimeLowering.pruneCulledSynthetics(outputDirectory); boolean concatenate = "true".equals(Util.getProperty("concatenateFiles", "false")); ConcatenatingFileOutputStream cos = concatenate ? new ConcatenatingFileOutputStream(outputDirectory) : null; diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/RuntimeLowering.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/RuntimeLowering.java index 3a15cbd5f39..48fe43b5ca4 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/RuntimeLowering.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/RuntimeLowering.java @@ -98,6 +98,7 @@ private RuntimeLowering() { static void cleanup() { UPDATERS.clear(); SERVICES.clear(); + CAS_BODIES.clear(); } static boolean isReplacedStub(String internalName) { @@ -294,6 +295,7 @@ static void writeSynthetics(File outputDirectory) throws IOException { boolean javascript = ByteCodeTranslator.output == ByteCodeTranslator.OutputType.OUTPUT_TYPE_JAVASCRIPT; StringBuilder c = new StringBuilder(); Set headers = new LinkedHashSet(); + CAS_BODIES.clear(); for (Updater u : UPDATERS.values()) { ByteCodeField field = findField(u.owner, u.field); if (field == null || field.isStaticField()) { @@ -307,7 +309,10 @@ static void writeSynthetics(File outputDirectory) throws IOException { if (!javascript) { headers.add(Util.mangle(u.owner)); headers.add(Util.mangle(u.generated)); + int before = c.length(); appendCasBody(c, u, field); + CAS_BODIES.add(new String[] {Util.mangle(u.owner), Util.mangle(u.generated), + c.substring(before)}); } } emitRegistry(); @@ -318,6 +323,44 @@ static void writeSynthetics(File outputDirectory) throws IOException { } return; } + writeCasFile(out, headers, c); + } + + /// Each CAS body written by writeSynthetics: the owner class, the generated + /// updater class and the C text. + private static final List CAS_BODIES = new ArrayList(); + + /// Rewrites the CAS file once the dead-code passes have run. It is written before + /// them so the natives it calls count as used, but an application that never + /// reaches an updater loses the owner or the updater class, and with it the header + /// the file includes: a Linux build without coroutines failed on a missing + /// kotlinx_coroutines_CancelledContinuation.h. Bodies whose classes are gone are + /// dropped, and the file with them when none is left. + static void pruneCulledSynthetics(File outputDirectory) throws IOException { + File out = new File(outputDirectory, GENERATED_C + "." + ByteCodeTranslator.output.extension()); + if (!out.exists() || CAS_BODIES.isEmpty()) { + return; + } + Set headers = new LinkedHashSet(); + StringBuilder c = new StringBuilder(); + for (String[] body : CAS_BODIES) { + if (Parser.getClassObject(body[0]) == null || Parser.getClassObject(body[1]) == null) { + continue; + } + headers.add(body[0]); + headers.add(body[1]); + c.append(body[2]); + } + if (c.length() == 0) { + if (!out.delete()) { + throw new IOException("Could not remove " + out); + } + return; + } + writeCasFile(out, headers, c); + } + + private static void writeCasFile(File out, Set headers, StringBuilder c) throws IOException { StringBuilder file = new StringBuilder(); file.append("// Generated by the translator: compare-and-set for the field updaters.\n"); file.append("#include \"cn1_globals.h\"\n#include \n"); From 2b43cb206d527e75bee7f87e5c314345e6823358 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 27 Oct 2026 07:24:07 +0200 Subject: [PATCH 15/32] Linux x64 and arm64 KmpCompat goldens Co-Authored-By: Claude Opus 5.5 --- .../linux/screenshots-arm/KmpCompatLayout.png | Bin 0 -> 7791 bytes scripts/linux/screenshots-arm/KmpCompatList.png | Bin 0 -> 30586 bytes .../linux/screenshots-arm/KmpCompatState.png | Bin 0 -> 8173 bytes .../screenshots-arm/KmpCompatTypography.png | Bin 0 -> 11140 bytes scripts/linux/screenshots/KmpCompatLayout.png | Bin 0 -> 7791 bytes scripts/linux/screenshots/KmpCompatList.png | Bin 0 -> 30586 bytes scripts/linux/screenshots/KmpCompatState.png | Bin 0 -> 8173 bytes .../linux/screenshots/KmpCompatTypography.png | Bin 0 -> 11140 bytes 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/linux/screenshots-arm/KmpCompatLayout.png create mode 100644 scripts/linux/screenshots-arm/KmpCompatList.png create mode 100644 scripts/linux/screenshots-arm/KmpCompatState.png create mode 100644 scripts/linux/screenshots-arm/KmpCompatTypography.png create mode 100644 scripts/linux/screenshots/KmpCompatLayout.png create mode 100644 scripts/linux/screenshots/KmpCompatList.png create mode 100644 scripts/linux/screenshots/KmpCompatState.png create mode 100644 scripts/linux/screenshots/KmpCompatTypography.png diff --git a/scripts/linux/screenshots-arm/KmpCompatLayout.png b/scripts/linux/screenshots-arm/KmpCompatLayout.png new file mode 100644 index 0000000000000000000000000000000000000000..4df2e854aa0f55e9689465e5a9226e0488d61577 GIT binary patch literal 7791 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 literal 0 HcmV?d00001 diff --git a/scripts/linux/screenshots-arm/KmpCompatTypography.png b/scripts/linux/screenshots-arm/KmpCompatTypography.png new file mode 100644 index 0000000000000000000000000000000000000000..c66a7de6ee99233d91bb914dd753a95dc31c82b6 GIT binary patch literal 11140 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 literal 0 HcmV?d00001 diff --git a/scripts/linux/screenshots/KmpCompatTypography.png b/scripts/linux/screenshots/KmpCompatTypography.png new file mode 100644 index 0000000000000000000000000000000000000000..c66a7de6ee99233d91bb914dd753a95dc31c82b6 GIT binary patch literal 11140 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 Date: Tue, 27 Oct 2026 19:36:19 +0200 Subject: [PATCH 16/32] Clip Compose children to the shape; fix GeneralPath.intersect on curves Modifier.clip clipped to the bounding rectangle, so a card's accent stripe kept square corners. It now clips to the rounded outline, cut to the clip already in force. That cut threw: BezierCurve.segment read the signed crossing count -1 of a curve running right to left as an unsupported curve order. Any rounded path crossing a clip edge failed to intersect. Also normalize a bound color override in one pass into a reused buffer. Co-Authored-By: Claude Opus 5.5 --- .../src/com/codename1/ui/geom/Geometry.java | 10 ++-- .../src/com/codename1/ui/plaf/UIManager.java | 55 +++++++++---------- .../com/codename1/compose/internal/UiNode.kt | 19 ++++++- .../codename1/ui/geom/GeneralPathTest.java | 23 ++++++++ 4 files changed, 73 insertions(+), 34 deletions(-) diff --git a/CodenameOne/src/com/codename1/ui/geom/Geometry.java b/CodenameOne/src/com/codename1/ui/geom/Geometry.java index 1bd48848fef..49fff4cf852 100644 --- a/CodenameOne/src/com/codename1/ui/geom/Geometry.java +++ b/CodenameOne/src/com/codename1/ui/geom/Geometry.java @@ -649,18 +649,20 @@ public void segment(Rectangle2D rect, List out) { n() == 3 ? ShapeUtil.intersectCubic(x[0], y[0], x[1], y[1], x[2], y[2], x[3], y[3], rect.getX(), rect.getY(), rect.getX() + rect.getWidth(), rect.getHeight() + rect.getY()) : n() == 1 ? ShapeUtil.intersectLine(x[0], y[0], x[1], y[1], rect.getX(), rect.getY(), rect.getX() + rect.getWidth(), rect.getY() + rect.getHeight()) : -1; - if (numIntersections == -1) { + if (n() < 1 || n() > 3) { throw new IllegalArgumentException("Cannot segment bezier curve of this order: " + n()); } + // The intersect functions return a signed crossing count (-1 for a + // curve running right to left) or CROSSING, not a number of points, + // so only zero is meaningful here. if (numIntersections == 0) { out.add(new BezierCurve(this)); return; } - - // To store t values of intersection points - double[] tvals = new double[numIntersections]; + // To store t values of intersection points: up to n() per edge. + double[] tvals = new double[4 * n()]; int nextTvalIndex = 0; double[] res = new double[3]; int numMatches = 0; diff --git a/CodenameOne/src/com/codename1/ui/plaf/UIManager.java b/CodenameOne/src/com/codename1/ui/plaf/UIManager.java index f72ec581647..541f9d89d2d 100644 --- a/CodenameOne/src/com/codename1/ui/plaf/UIManager.java +++ b/CodenameOne/src/com/codename1/ui/plaf/UIManager.java @@ -2709,6 +2709,9 @@ private void syncBoundRoundBorderColor(String themeKey, String colorValue, boole } } + /// Scratch for `normalizeBoundColorValue`, which always produces 6 digits. + private static final char[] HEX_COLOR_BUFFER = new char[6]; + /// `loadTheme` stores color theme entries as plain hex strings (no `#`, /// lowercase). User-supplied overrides may use either form, so trim a /// leading `#` and lowercase the value before assigning it to a bound @@ -2719,40 +2722,34 @@ private static String normalizeBoundColorValue(String raw) { return null; } String value = raw.trim(); - if (value.length() == 0) { - return null; - } - if (value.charAt(0) == '#') { - value = value.substring(1); - } - if (value.length() == 3) { - char r = value.charAt(0); - char g = value.charAt(1); - char b = value.charAt(2); - value = "" + r + r + g + g + b + b; - } - if (value.length() != 6) { + int len = value.length(); + int start = len > 0 && value.charAt(0) == '#' ? 1 : 0; + int digits = len - start; + if (digits != 3 && digits != 6) { return null; } - boolean upper = false; - for (int i = 0; i < 6; i++) { - char c = value.charAt(i); - boolean hex = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); - if (!hex) { + // One pass into a reused buffer: validate, expand the 3-digit form and + // fold hex digits by hand, since toLowerCase() is locale sensitive. + // Themes are installed from one thread at a time, as the rest of + // UIManager assumes. + char[] c = HEX_COLOR_BUFFER; + boolean changed = start != 0 || digits == 3; + for (int i = 0; i < digits; i++) { + char x = value.charAt(start + i); + if (x >= 'A' && x <= 'F') { + x = (char) (x + 32); + changed = true; + } else if (!((x >= '0' && x <= '9') || (x >= 'a' && x <= 'f'))) { return null; } - upper |= c >= 'A' && c <= 'F'; - } - if (!upper) { - return value; - } - // Hex digits, folded by hand: toLowerCase() is locale sensitive. - char[] c = new char[6]; - for (int i = 0; i < 6; i++) { - char x = value.charAt(i); - c[i] = x >= 'A' && x <= 'F' ? (char) (x + ('a' - 'A')) : x; + if (digits == 3) { + c[2 * i] = x; + c[2 * i + 1] = x; + } else { + c[i] = x; + } } - return new String(c); + return changed ? new String(c) : value; } private Map parseCache() { diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt index 22a95da6fb1..d7f6e666ac1 100644 --- a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt @@ -550,7 +550,8 @@ internal open class UiNode : Container(NodeLayout) { val clip = clipBox() if (clip >= 0) { g.pushClip() - g.clipRect(x + boxes[clip * 4], y + boxes[clip * 4 + 1], boxes[clip * 4 + 2], boxes[clip * 4 + 3]) + clipTo(g, (chain[clip] as ClipElement).shape, x + boxes[clip * 4], y + boxes[clip * 4 + 1], + boxes[clip * 4 + 2], boxes[clip * 4 + 3]) } paintContent(g) if (clip >= 0) { @@ -571,6 +572,22 @@ internal open class UiNode : Container(NodeLayout) { super.paint(g) } + /// Clips to `shape`, as Compose does: a card's children stay inside its + /// rounded corners. `setClip(Shape)` replaces the clip, so the path is + /// first cut to the clip in force, or a card scrolled under a bar would + /// paint over it. + private fun clipTo(g: Graphics, shape: Shape, cx: Int, cy: Int, cw: Int, ch: Int) { + val o = Painting.outline(shape, cw, ch) + if (o is androidx.compose.ui.graphics.Outline.Rounded && g.isShapeClipSupported) { + val path = Painting.roundedPath(o, cx.toFloat(), cy.toFloat()) + if (path.intersect(g.clipX, g.clipY, g.clipWidth, g.clipHeight)) { + g.setClip(path) + return + } + } + g.clipRect(cx, cy, cw, ch) + } + private fun clipBox(): Int { for (i in chain.indices) { if (chain[i] is ClipElement) { diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/geom/GeneralPathTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/geom/GeneralPathTest.java index c7389150fdf..9f1ad248463 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/geom/GeneralPathTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/geom/GeneralPathTest.java @@ -191,6 +191,29 @@ void testContainsAndIntersectsRectangle() { assertFalse(path.intersect(noOverlap)); } + /// A rounded rectangle cut by a clip that crosses its corners: the curves + /// running right to left made the crossing count -1, which segmenting + /// mistook for an unsupported curve order and threw. + @Test + void testIntersectRoundedRectangleAcrossCorners() { + GeneralPath path = new GeneralPath(); + path.moveTo(10, 0); + path.lineTo(90, 0); + path.quadTo(100, 0, 100, 10); + path.lineTo(100, 40); + path.quadTo(100, 50, 90, 50); + path.lineTo(10, 50); + path.quadTo(0, 50, 0, 40); + path.lineTo(0, 10); + path.quadTo(0, 0, 10, 0); + path.closePath(); + assertTrue(path.intersect(new Rectangle(-5, 5, 110, 30))); + Rectangle bounds = path.getBounds(); + assertEquals(0, bounds.getX()); + assertEquals(100, bounds.getX() + bounds.getWidth()); + assertTrue(bounds.getY() >= 5 && bounds.getY() + bounds.getHeight() <= 35); + } + @Test void testEqualsWithTransform() { GeneralPath original = new GeneralPath(); From 41a763e3b095c4333b3726dd111071d35f6b23f7 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:36:19 +0200 Subject: [PATCH 17/32] kmp-bench: stripped bundle sizes, recomposition and scroll timings Co-Authored-By: Claude Opus 5.5 --- scripts/kmp-bench/README.md | 48 ++++++-- .../cmp/composeApp/src/iosMain/kotlin/Main.kt | 6 +- scripts/kmp-bench/app/cn1/BenchEntry.kt | 4 +- .../shared/commonMain/kotlin/keynote/App.kt | 26 ++++- .../shared/commonMain/kotlin/keynote/Perf.kt | 105 ++++++++++++++++++ scripts/kmp-bench/run_bench.sh | 60 +++++++++- 6 files changed, 227 insertions(+), 22 deletions(-) create mode 100644 scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/Perf.kt diff --git a/scripts/kmp-bench/README.md b/scripts/kmp-bench/README.md index 0caa7dd4dbb..83fca6daa52 100644 --- a/scripts/kmp-bench/README.md +++ b/scripts/kmp-bench/README.md @@ -37,7 +37,9 @@ each app into the work directory. |---|---| | Start-up | From `simctl launch` to the app's `KMPBENCH first-frame` line. Both print it on the first main-thread turn after their UI is installed. CMP's marker fires before Skia has drawn anything, so the comparison favours CMP. Run 1 is the first launch after install (cold); the median of the rest is reported as warm. | | Executable | The app binary after `strip`, which is what an App Store build ships. | -| Bundle | The `.app` directory as built (the CN1 binary in it is not yet stripped). | +| Bundle | A copy of the `.app` with its executable stripped, plus the bytes of everything in it except the executable. | +| Recomposition | `KMPBENCH_PERF=1` launches only (`Perf.kt`, shared code). After 30 discarded changes, the day filter is changed 300 times. Each change is timed until a 1 dp marker drawn after the list has been painted, so compose, layout and draw are all included. | +| Scroll | After one discarded pass, all 150 cards (three days of 50) are scrolled one card per frame with `scrollToItem`. compose-compat has no `scrollBy`. Work is the time from the scroll call to the marker drawn. Interval is the time between consecutive markers. Jank is frames longer than twice the median interval. | | Memory | Physical footprint (`footprint `) five seconds after launch: the dirty memory the process owns, which is what iOS charges an app and kills it for. Resident size is not used: on the simulator it counts the shared dyld cache, which every process maps, so an empty app already shows 184 MB. | ## Results @@ -46,12 +48,38 @@ iPhone 17 Pro simulator, Xcode 27, Apple silicon, Release builds, 5 runs. Start-up here is from process start (the kernel's start time) to the marker, so `simctl`'s own overhead is left out of both. -| | Empty UIKit app | Codename One | Compose Multiplatform | -|---|---|---|---| -| Start-up | 640-750 ms | 596-685 ms | 646-794 ms | -| Executable, stripped | | 16.4 MB | 22.9 MB | -| App bundle | | 27.3 MB | 28.8 MB | -| Physical footprint | 34 MB | 47-48 MB | 48 MB | +| | Codename One | Compose Multiplatform | +|---|---|---| +| Cold start | 1075 ms | 1349 ms | +| Warm start (median) | 623 ms | 840 ms | +| Executable, stripped | 16.8 MB | 22.9 MB | +| App bundle, executable stripped | 18.2 MB | 22.9 MB | +| Bundle without executable | 1.3 MB | 2.5 KB | +| Physical footprint | 49 MB | 48 MB | +| Recomposition, day change to drawn (median / p95) | 42.6 / 47.0 ms | 20.2 / 23.3 ms | +| Scroll, work per frame (median / p95) | 40.0 / 43.4 ms | 16.8 / 18.0 ms | +| Scroll, frame interval (median / p95) | 51.2 / 55.1 ms | 33.4 / 34.2 ms | +| Scroll, frames over 2x median | 0 of 144 | 0 of 144 | + +Runtime values are the median over 5 launches of each launch's median or p95. +An empty UIKit app measured 640-750 ms start-up and a 34 MB footprint on the +same simulator. + +**The bundles differ mostly in resources.** The CMP bundle holds only the +executable, `Info.plist` and its signature, because Skia and the fonts it uses +are linked in. The CN1 bundle holds 1.3 MB besides its executable: +`Assets.car` (570 KB), `material-design-font.ttf` (357 KB), +`iOSModernTheme.res` (213 KB), `default.metallib` (92 KB) and +`kotlin.kotlin_builtins` (29 KB). + +**CMP does each step in about half the time.** A day change takes about 42 ms +from state change to drawn on Codename One and 20 ms on CMP. One card of +scrolling takes 40 ms against 17 ms. Neither side dropped a frame against its own +median, but Codename One's frame interval (51 ms) is well past the simulator's +display interval, while CMP's is about two display frames (33 ms). These are +simulator numbers on an Apple-silicon Mac. They show where each runtime spends +CPU in identical code. They do not show device frame rates, GPU cost, or how a +finger-driven fling feels. **Start-up is the simulator's floor.** An empty UIKit application, with no code of its own, takes as long to show its first frame as either side, so @@ -59,7 +87,7 @@ neither has much start-up of its own left to lose there. Measure on a device before quoting a difference. The Codename One executable includes the whole iOS port and the full Material -icon font, so the 6.5 MB difference is a floor. +icon font, so the 6.1 MB difference is a floor. **Memory is level.** The Java heap holds 3.2 MB live after start-up, in about 57,000 objects. Loading the theme used to @@ -81,5 +109,5 @@ here yet. - `LazyColumn` and `LazyRow` with `reverseLayout = true` are refused at run time. The lists compose only the items in view. -- Frame time while scrolling is not measured. That needs a device and a driven - gesture rather than a simulator launch. +- Scrolling is measured as programmatic `scrollToItem` steps on the simulator, + not a touch fling on a device. diff --git a/scripts/kmp-bench/app/cmp/composeApp/src/iosMain/kotlin/Main.kt b/scripts/kmp-bench/app/cmp/composeApp/src/iosMain/kotlin/Main.kt index 020d310ab39..684342717ff 100644 --- a/scripts/kmp-bench/app/cmp/composeApp/src/iosMain/kotlin/Main.kt +++ b/scripts/kmp-bench/app/cmp/composeApp/src/iosMain/kotlin/Main.kt @@ -44,6 +44,8 @@ import kotlinx.cinterop.autoreleasepool import kotlinx.cinterop.cstr import kotlinx.cinterop.memScoped import kotlinx.cinterop.toCValues +import kotlinx.cinterop.toKString +import platform.posix.getenv /// The whole iOS application: a delegate whose window shows the composition. /// An executable rather than a framework inside an Xcode project, so the build @@ -55,8 +57,10 @@ class AppDelegate @OverrideInit constructor() : UIResponder(), UIApplicationDele private var mainWindow: UIWindow? = null override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map?): Boolean { + // run_bench.sh launches with SIMCTL_CHILD_KMPBENCH_PERF=1 for the runtime measurements. + val perf = getenv("KMPBENCH_PERF")?.toKString() == "1" val w = UIWindow(frame = UIScreen.mainScreen.bounds) - w.rootViewController = ComposeUIViewController { App() } + w.rootViewController = ComposeUIViewController { App(perf = perf) } w.makeKeyAndVisible() mainWindow = w // After the launch method returns, as the Codename One side prints its diff --git a/scripts/kmp-bench/app/cn1/BenchEntry.kt b/scripts/kmp-bench/app/cn1/BenchEntry.kt index 6044aee312a..fccf31403b8 100644 --- a/scripts/kmp-bench/app/cn1/BenchEntry.kt +++ b/scripts/kmp-bench/app/cn1/BenchEntry.kt @@ -30,7 +30,9 @@ import keynote.App /// Shows the application and prints the marker the harness times start-up /// against, once the first frame with content has been painted. fun start() { - val form = ComposeForm { App() } + // run_bench.sh launches with SIMCTL_CHILD_KMPBENCH_PERF=1 for the runtime measurements. + val perf = System.getenv("KMPBENCH_PERF") == "1" + val form = ComposeForm { App(perf = perf) } form.show() CN.callSerially { println("KMPBENCH first-frame " + System.currentTimeMillis()) diff --git a/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/App.kt b/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/App.kt index e82b964666f..02185b5a036 100644 --- a/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/App.kt +++ b/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/App.kt @@ -44,6 +44,9 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.ui.draw.drawBehind import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -100,6 +103,12 @@ class AppState { var selected by mutableStateOf(null) val favorites = mutableStateListOf() + // Benchmark hooks (Perf.kt); unused in a normal launch. + var list: LazyListState? = null + var tick by mutableIntStateOf(0) + var drawnSeq = 0 + var drawnAt = 0L + fun toggleFavorite(id: Int) { if (!favorites.remove(id)) favorites.add(id) } @@ -108,12 +117,13 @@ class AppState { private val Brand = Color(0xFF7F52FF) @Composable -fun App(state: AppState = remember { AppState() }) { +fun App(state: AppState = remember { AppState() }, perf: Boolean = false) { val dark = isSystemInDarkTheme() val scheme = if (dark) darkColorScheme(primary = Color(0xFFB9A4FF), secondaryContainer = Color(0xFF3A2D6B)) else lightColorScheme(primary = Brand, secondaryContainer = Color(0xFFE9E1FF)) MaterialTheme(colorScheme = scheme) { val sessions by remember { Programme.load() }.collectAsState(emptyList()) + if (perf && sessions.isNotEmpty()) LaunchedEffect(Unit) { runPerf(state) } val selected = state.selected if (selected != null) { SessionDetail(selected, state.favorites.contains(selected.id), { state.toggleFavorite(selected.id) }) { @@ -167,19 +177,27 @@ private fun Schedule(sessions: List, state: AppState) { it.day == state.day && (q.isEmpty() || it.title.contains(q, ignoreCase = true) || it.speaker.name.contains(q, ignoreCase = true)) } - SessionList(visible, state, empty = "No session matches \"$q\".") + val listState = rememberLazyListState() + state.list = listState + Box(Modifier.weight(1f)) { + SessionList(visible, state, empty = "No session matches \"$q\".", listState = listState) + } + // Drawn after the list in every frame that follows a benchmark step. + val seq = state.tick + Box(Modifier.size(1.dp).drawBehind { state.drawnAt = nowNanos(); state.drawnSeq = seq }) } } @Composable -private fun SessionList(sessions: List, state: AppState, empty: String) { +private fun SessionList(sessions: List, state: AppState, empty: String, + listState: LazyListState = rememberLazyListState()) { if (sessions.isEmpty()) { Box(Modifier.fillMaxSize().padding(32.dp), contentAlignment = Alignment.Center) { Text(empty, color = MaterialTheme.colorScheme.onSurfaceVariant) } return } - LazyColumn(Modifier.fillMaxSize().testTag("list"), contentPadding = PaddingValues(16.dp, 8.dp), + LazyColumn(Modifier.fillMaxSize().testTag("list"), state = listState, contentPadding = PaddingValues(16.dp, 8.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { items(sessions, key = { it.id }) { s -> SessionCard(s, state.favorites.contains(s.id), { state.toggleFavorite(s.id) }) { state.selected = s } diff --git a/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/Perf.kt b/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/Perf.kt new file mode 100644 index 00000000000..ba03c737cba --- /dev/null +++ b/scripts/kmp-bench/app/shared/commonMain/kotlin/keynote/Perf.kt @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package keynote + +import androidx.compose.runtime.withFrameNanos +import kotlin.time.TimeSource + +/// CPU-bound measurements run inside the app, identical on both sides. Each +/// result is printed as "KMPBENCH " for run_bench.sh. +/// +/// Time is taken from a state change to the moment a 1 dp marker after the list +/// is drawn (`AppState.drawnAt`), so it covers recomposition, layout and drawing +/// of that frame. The simulator paces frames to its display, so frame interval +/// alone mostly shows vsync; the change-to-drawn time is the work itself. +private val origin = TimeSource.Monotonic.markNow() +internal fun nowNanos(): Long = origin.elapsedNow().inWholeNanoseconds + +private suspend fun awaitDrawn(state: AppState, seq: Int) { + var guard = 0 + while (state.drawnSeq < seq) { + if (guard++ == 60) { println("KMPBENCH perf-stall " + seq); throw IllegalStateException("marker not drawn") } + withFrameNanos { } + } +} + +private fun pct(v: List, p: Int): Long { + val s = v.sorted() + return s[minOf(s.size - 1, (s.size * p) / 100)] +} + +private fun report(name: String, v: Long) = println("KMPBENCH $name $v") + +private suspend fun recompose(state: AppState, n: Int): List { + val out = ArrayList() + for (i in 0 until n) { + withFrameNanos { } + val t0 = nowNanos() + state.day = state.day % 3 + 1 + val seq = ++state.tick + awaitDrawn(state, seq) + out.add((state.drawnAt - t0) / 1000) + } + return out +} + +/// Scrolls the list of the current day one card per frame, to the end. +private suspend fun scrollDay(state: AppState, work: MutableList, frames: MutableList) { + val list = state.list ?: return + list.scrollToItem(0) + withFrameNanos { } + val count = 50 // sessions per day, Data.kt + var last = 0L + for (i in 1 until count) { + withFrameNanos { } + val t0 = nowNanos() + list.scrollToItem(i) + val seq = ++state.tick + awaitDrawn(state, seq) + work.add((state.drawnAt - t0) / 1000) + if (last != 0L) frames.add((state.drawnAt - last) / 1000) + last = state.drawnAt + } +} + +suspend fun runPerf(state: AppState) { + println("KMPBENCH perf-start 0") + repeat(10) { withFrameNanos { } } + recompose(state, 30) // warm-up, discarded + val r = recompose(state, 300) + report("recompose-median-us", pct(r, 50)); report("recompose-p95-us", pct(r, 95)) + state.day = 1; withFrameNanos { }; withFrameNanos { } + scrollDay(state, ArrayList(), ArrayList()) // warm-up, discarded + val work = ArrayList(); val frames = ArrayList() + for (d in 1..3) { + state.day = d; withFrameNanos { }; withFrameNanos { } + scrollDay(state, work, frames) + } + val fm = pct(frames, 50) + report("scroll-frames", frames.size.toLong()) + report("scroll-frame-median-us", fm); report("scroll-frame-p95-us", pct(frames, 95)) + report("scroll-jank-frames", frames.count { it > 2 * fm }.toLong()) + report("scroll-work-median-us", pct(work, 50)); report("scroll-work-p95-us", pct(work, 95)) + report("perf-done", 0) +} diff --git a/scripts/kmp-bench/run_bench.sh b/scripts/kmp-bench/run_bench.sh index 2d21b149d00..3167b1d0997 100755 --- a/scripts/kmp-bench/run_bench.sh +++ b/scripts/kmp-bench/run_bench.sh @@ -81,7 +81,15 @@ cp "$HERE/app/cmp/Info.plist" "$CMP_APP/Info.plist" codesign -s - --force "$CMP_APP" > /dev/null 2>&1 stripped() { cp "$1" "$WORK/strip.tmp"; strip "$WORK/strip.tmp" 2>/dev/null || true; stat -f %z "$WORK/strip.tmp"; } -bundle() { du -sk "$1" | awk '{print $1 * 1024}'; } +# The bundle as shipped: a copy of the .app with its executable stripped. +bundle() { + rm -rf "$WORK/bundle.tmp"; cp -R "$1" "$WORK/bundle.tmp" + strip "$WORK/bundle.tmp/$2" 2>/dev/null || true + du -sk "$WORK/bundle.tmp" | awk '{print $1 * 1024}' +} +# Bytes of everything in the bundle but the executable (fonts, theme, plists). +resources() { find "$1" -type f ! -path "$1/$2" -exec stat -f %z {} + | awk '{s += $1} END {print s + 0}'; } +largest() { find "$1" -type f ! -path "$1/$2" -exec stat -f '%z %N' {} + | sort -rn | head -5 | sed "s|$1/||"; } if [ -z "$DEVICE" ]; then DEVICE="$(xcrun simctl list devices available | sed -n 's/.*iPhone 17 Pro (\([0-9A-F-]*\)).*/\1/p' | head -1)" @@ -135,27 +143,67 @@ for side in cn1 cmp; do xcrun simctl install "$DEVICE" "$app" eval "STARTS_$side=\"\$(measure \"\$id\" \"\$side\" \"\$exe\")\"" done +# Runtime measurements (app/shared/commonMain/kotlin/keynote/Perf.kt): one +# launch per run with KMPBENCH_PERF=1, values in microseconds, collected as one +# "name=v1,v2,..." line per measure. +perf() { + local id="$1" name="$2" + for i in $(seq 1 "$RUNS"); do + xcrun simctl terminate "$DEVICE" "$id" 2>/dev/null || true + local log="$WORK/$name-perf$i.log" + ( SIMCTL_CHILD_KMPBENCH_PERF=1 timeout 180 xcrun simctl launch --console-pty "$DEVICE" "$id" > "$log" 2>&1 || true ) & + for _ in $(seq 1 1700); do grep -q "KMPBENCH perf-done" "$log" 2>/dev/null && break; sleep 0.1; done + grep -q "KMPBENCH perf-done" "$log" || { echo "$name perf run $i did not finish; see $log" >&2; exit 1; } + xcrun simctl terminate "$DEVICE" "$id" 2>/dev/null || true + done + cat "$WORK/$name"-perf[0-9]*.log | tr -d '\r' | awk '{ for (j = 1; j < NF; j++) if ($j == "KMPBENCH") break } + j < NF && $(j + 1) !~ /^(perf-done|perf-start|first-frame)$/ { + v[$(j + 1)] = v[$(j + 1)] (v[$(j + 1)] == "" ? "" : ",") $(j + 2) } END { for (k in v) print k "=" v[k] }' > "$WORK/$name-perf.txt" +} +perf $CN1_ID cn1; perf $CMP_ID cmp CN1_RSS="$(footprint_bytes $CN1_ID Bench.app/Bench)"; CMP_RSS="$(footprint_bytes $CMP_ID Keynote.app/Keynote)" python3 - "$WORK" < Date: Tue, 27 Oct 2026 19:38:13 +0200 Subject: [PATCH 18/32] compose-compat: a clip that misses the rounded outline clips to nothing Co-Authored-By: Claude Opus 5.5 --- .../main/kotlin/com/codename1/compose/internal/UiNode.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt index d7f6e666ac1..81670ba8efd 100644 --- a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt @@ -582,8 +582,12 @@ internal open class UiNode : Container(NodeLayout) { val path = Painting.roundedPath(o, cx.toFloat(), cy.toFloat()) if (path.intersect(g.clipX, g.clipY, g.clipWidth, g.clipHeight)) { g.setClip(path) - return + } else { + // The clip in force misses the outline (it may still meet a + // corner the rounding cuts off): nothing is visible. + g.clipRect(0, 0, 0, 0) } + return } g.clipRect(cx, cy, cw, ch) } From 4dc9f9a1507f4860c0ac6f2210dccf5b0d973f13 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:49:03 +0200 Subject: [PATCH 19/32] compose-compat: deliver frames at layout time, dispatch inline on the EDT, clip rounded shapes as polygons Recomposition median 42.6ms -> 3.8ms and scroll work 40ms -> 3.3ms on the Keynote benchmark: the pipeline lost two EDT cycles of latency, and the rounded clip no longer allocates curve intersections every paint. Co-Authored-By: Claude Opus 5.5 --- .../codename1/compose/internal/ComposeHost.kt | 61 ++++++++-- .../codename1/compose/internal/Painting.kt | 111 ++++++++++++++++++ .../com/codename1/compose/internal/UiNode.kt | 5 +- 3 files changed, 163 insertions(+), 14 deletions(-) diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt index 0ddb3893145..1c94e1d4185 100644 --- a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt @@ -30,7 +30,6 @@ import androidx.compose.runtime.Composition import androidx.compose.runtime.Recomposer import com.codename1.ui.Component import com.codename1.ui.Container -import com.codename1.ui.Form import com.codename1.ui.geom.Dimension import com.codename1.ui.layouts.Layout import kotlinx.coroutines.CoroutineScope @@ -99,10 +98,17 @@ internal class NodeApplier(root: Container) : AbstractApplier(root) { /// composition's top level nodes, stacked as a `Box` would stack them. internal class ComposeHost : Container(HostLayout) { private val clock = BroadcastFrameClock { scheduleFrame() } - private val scope = CoroutineScope(EdtDispatcher.Main + clock + Job()) + // Immediate, so a frame resumes its awaiters inside sendFrame. Dispatched, + // every resumption waited for the end of the EDT cycle, and so did the + // state it wrote: a change reached the screen three cycles after it was + // made. Now the recomposer applies what a frame callback writes in that + // same frame (see scheduleFrame for when the frame runs). + private val scope = CoroutineScope(EdtDispatcher.Immediate + clock + Job()) private val recomposer = Recomposer(scope.coroutineContext) private var composition: Composition? = null - private var animatedOn: Form? = null + private var inFrame = false + private var sentInLayout = false + private var animatedOn: com.codename1.ui.Form? = null private var frameRequested = false private var timerPending = false @@ -134,6 +140,15 @@ internal class ComposeHost : Container(HostLayout) { // --- frames ------------------------------------------------------------- + /// A frame runs at the start of the next EDT cycle's layout pass, as + /// Android's Choreographer runs a frame's callbacks before traversal: the + /// frame, recomposition, layout and paint then share one cycle. Sent from + /// `animate()`, which runs after the cycle has painted, a change waited a + /// whole cycle more. The cycle's own wait paces continuous animation: + /// `revalidateLater` does not wake the EDT. `animate()`, after the paint, + /// is the fallback for a cycle whose layout pass did not reach this host + /// (the form defers revalidation while it animates); the timer is for a + /// host that is not showing. A timer per frame cost a thread per frame. private fun scheduleFrame() { frameRequested = true val f = componentForm @@ -143,6 +158,9 @@ internal class ComposeHost : Container(HostLayout) { f.registerAnimated(this) animatedOn = f } + if (!inFrame) { + revalidateLater() + } return } if (!timerPending) { @@ -161,11 +179,22 @@ internal class ComposeHost : Container(HostLayout) { } } + override fun animate(): Boolean { - sendFrame() + if (sentInLayout) { + sentInLayout = false + } else { + sendFrame() + } return false } + override fun deinitialize() { + animatedOn?.deregisterAnimated(this) + animatedOn = null + super.deinitialize() + } + override fun paint(g: com.codename1.ui.Graphics) { super.paint(g) } @@ -177,17 +206,15 @@ internal class ComposeHost : Container(HostLayout) { } } - override fun deinitialize() { - animatedOn?.deregisterAnimated(this) - animatedOn = null - super.deinitialize() - } - // --- measuring ---------------------------------------------------------- private var remeasurePending = false fun requestRemeasure() { + if (inFrame) { + // This pass measures what the frame applies. + return + } if (!remeasurePending) { remeasurePending = true setShouldCalcPreferredSize(true) @@ -204,6 +231,20 @@ internal class ComposeHost : Container(HostLayout) { private var insetsKnown = false internal fun layoutChildren() { + if (frameRequested) { + // What the frame applies is measured by this pass, so it must not + // queue another; a frame asked for during it goes to the next cycle. + inFrame = true + try { + sendFrame() + } finally { + inFrame = false + } + sentInLayout = true + if (frameRequested) { + revalidateLater() + } + } remeasurePending = false val insets = SafeInsets.of(absoluteY, height) val top = (insets ushr 32).toInt() diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Painting.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Painting.kt index 85696f6e843..e7b61c7dc09 100644 --- a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Painting.kt +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/Painting.kt @@ -149,6 +149,117 @@ internal object Painting { return p } + /// Clips `g` to the rounded rectangle `o` at (`x`, `y`), intersected with + /// the clip already in force, and answers false when nothing of it is + /// visible. `setClip(Shape)` replaces the clip, so the intersection is + /// ours to make. A rounded rectangle is convex: flattened to a polygon it + /// is cut by the clip rectangle edge by edge (Sutherland-Hodgman), with no + /// curve math and no allocation once the buffers have grown. Cutting the + /// curved path with `GeneralPath.intersect` was most of what a scrolling + /// list allocated; the iOS port flattens a curved clip to a polygon anyway. + fun clipRounded(g: Graphics, o: Outline.Rounded, x: Int, y: Int): Boolean { + var n = roundedPolygon(o, x.toFloat(), y.toFloat()) + val left = g.clipX.toFloat() + val top = g.clipY.toFloat() + val right = left + g.clipWidth + val bottom = top + g.clipHeight + n = cut(n, 0, left, true) + n = cut(n, 0, right, false) + n = cut(n, 1, top, true) + n = cut(n, 1, bottom, false) + if (n < 3) { + return false + } + val p = clipPath + p.reset() + p.moveTo(poly[0], poly[1]) + for (i in 1 until n) { + p.lineTo(poly[i * 2], poly[i * 2 + 1]) + } + p.closePath() + g.setClip(p) + return true + } + + private const val ARC_STEPS = 6 + private var poly = FloatArray(64) + private var scratch = FloatArray(64) + private val clipPath = GeneralPath() + + /// Writes `o` as a clockwise polygon into `poly`; answers its point count. + private fun roundedPolygon(o: Outline.Rounded, x: Float, y: Float): Int { + val max = Math.min(o.width, o.height) / 2f + val w = o.width + val h = o.height + ensure(4 * (ARC_STEPS + 1) + 8) + var n = 0 + n = corner(n, x, y, Math.min(o.topLeft, max), 1f, 1f, PI) + n = corner(n, x + w, y, Math.min(o.topRight, max), -1f, 1f, PI + HALF_PI) + n = corner(n, x + w, y + h, Math.min(o.bottomRight, max), -1f, -1f, 0f) + n = corner(n, x, y + h, Math.min(o.bottomLeft, max), 1f, -1f, HALF_PI) + return n + } + + private fun corner(n: Int, px: Float, py: Float, r: Float, sx: Float, sy: Float, start: Float): Int { + var k = n + if (r <= 0f) { + poly[k * 2] = px + poly[k * 2 + 1] = py + return k + 1 + } + val cx = px + sx * r + val cy = py + sy * r + for (i in 0..ARC_STEPS) { + val a = start + HALF_PI * i / ARC_STEPS + poly[k * 2] = cx + r * Math.cos(a.toDouble()).toFloat() + poly[k * 2 + 1] = cy + r * Math.sin(a.toDouble()).toFloat() + k++ + } + return k + } + + /// Keeps the part of the polygon in `poly` on the inner side of the line + /// `axis` (0 is x, 1 is y) = `v`; `low` keeps values at or above it. + private fun cut(n: Int, axis: Int, v: Float, low: Boolean): Int { + if (n == 0) { + return 0 + } + ensure(n + 4) + val src = poly + val dst = scratch + var m = 0 + for (i in 0 until n) { + val j = (i + 1) % n + val a = src[i * 2 + axis] + val b = src[j * 2 + axis] + val aIn = if (low) a >= v else a <= v + val bIn = if (low) b >= v else b <= v + if (aIn) { + dst[m * 2] = src[i * 2] + dst[m * 2 + 1] = src[i * 2 + 1] + m++ + } + if (aIn != bIn) { + val t = (v - a) / (b - a) + dst[m * 2] = src[i * 2] + t * (src[j * 2] - src[i * 2]) + dst[m * 2 + 1] = src[i * 2 + 1] + t * (src[j * 2 + 1] - src[i * 2 + 1]) + m++ + } + } + poly = dst + scratch = src + return m + } + + private fun ensure(points: Int) { + if (poly.size < points * 2) { + poly = poly.copyOf(points * 4) + } + if (scratch.size < points * 2 || scratch.size < poly.size) { + scratch = FloatArray(poly.size) + } + } + private const val PI = Math.PI.toFloat() private const val HALF_PI = (Math.PI / 2).toFloat() } diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt index 81670ba8efd..9a1250696c3 100644 --- a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/UiNode.kt @@ -579,10 +579,7 @@ internal open class UiNode : Container(NodeLayout) { private fun clipTo(g: Graphics, shape: Shape, cx: Int, cy: Int, cw: Int, ch: Int) { val o = Painting.outline(shape, cw, ch) if (o is androidx.compose.ui.graphics.Outline.Rounded && g.isShapeClipSupported) { - val path = Painting.roundedPath(o, cx.toFloat(), cy.toFloat()) - if (path.intersect(g.clipX, g.clipY, g.clipWidth, g.clipHeight)) { - g.setClip(path) - } else { + if (!Painting.clipRounded(g, o, cx, cy)) { // The clip in force misses the outline (it may still meet a // corner the rounding cuts off): nothing is visible. g.clipRect(0, 0, 0, 0) From 7c00a6ee9a6a5017721ed325d81f506dc43a601c Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:52:20 +0200 Subject: [PATCH 20/32] compose-compat: a frame that resizes the host asks its parent again; no frames for a hidden host Co-Authored-By: Claude Opus 5.5 --- .../codename1/compose/internal/ComposeHost.kt | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt index 1c94e1d4185..c2e686e75fe 100644 --- a/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt +++ b/maven/compose-compat/src/main/kotlin/com/codename1/compose/internal/ComposeHost.kt @@ -110,7 +110,7 @@ internal class ComposeHost : Container(HostLayout) { private var sentInLayout = false private var animatedOn: com.codename1.ui.Form? = null private var frameRequested = false - private var timerPending = false + private var remeasureInFrame = false init { cn1StripStyle(this) @@ -147,8 +147,10 @@ internal class ComposeHost : Container(HostLayout) { /// whole cycle more. The cycle's own wait paces continuous animation: /// `revalidateLater` does not wake the EDT. `animate()`, after the paint, /// is the fallback for a cycle whose layout pass did not reach this host - /// (the form defers revalidation while it animates); the timer is for a - /// host that is not showing. A timer per frame cost a thread per frame. + /// (the form defers revalidation while it animates). A host that is not + /// showing gets no frames: its request waits, and `initComponent` sends it + /// when the host is shown again, so a hidden infinite animation costs + /// nothing. private fun scheduleFrame() { frameRequested = true val f = componentForm @@ -161,14 +163,6 @@ internal class ComposeHost : Container(HostLayout) { if (!inFrame) { revalidateLater() } - return - } - if (!timerPending) { - timerPending = true - com.codename1.ui.CN.setTimeout(16) { - timerPending = false - sendFrame() - } } } @@ -212,7 +206,9 @@ internal class ComposeHost : Container(HostLayout) { fun requestRemeasure() { if (inFrame) { - // This pass measures what the frame applies. + // This pass lays out what the frame applies, but the parent sized + // the host before it; layoutChildren asks the parent again. + remeasureInFrame = true return } if (!remeasurePending) { @@ -241,6 +237,20 @@ internal class ComposeHost : Container(HostLayout) { inFrame = false } sentInLayout = true + if (remeasureInFrame) { + // Next cycle, never inline: the parent is mid-layout. + // Only when the size really moved: most frames change content, + // not size, and a parent pass per frame is what this avoids. + remeasureInFrame = false + val before = preferredSize + val w = before.width + val h = before.height + setShouldCalcPreferredSize(true) + val after = preferredSize + if (after.width != w || after.height != h) { + parent?.revalidateLater() + } + } if (frameRequested) { revalidateLater() } From b81e9096da48a8092c6b29342288364f106e47f1 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 04:02:53 +0200 Subject: [PATCH 21/32] kmp-bench: relative chart, final numbers, guide motivation backed by them Co-Authored-By: Claude Opus 5.5 --- .../Kotlin-Multiplatform-Interop.asciidoc | 49 +++++---- .../developer-guide/img/kmp-bench-results.svg | 66 ++++++------ .../img/kmp-keynote-schedule.png | Bin 302200 -> 271677 bytes scripts/kmp-bench/README.md | 36 ++++--- scripts/kmp-bench/chart.py | 98 ++++++++++++++++++ 5 files changed, 185 insertions(+), 64 deletions(-) create mode 100755 scripts/kmp-bench/chart.py diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc index 7c5c5398207..c95c9ca1f49 100644 --- a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -10,30 +10,33 @@ watchOS and tvOS. === Motivation -Compose Multiplatform draws its whole user interface with Skia, and on iOS it -runs on Kotlin/Native. Both come with the application: a large native -runtime, a drawing engine that doesn't use the platform's text input, -accessibility or look, and a second toolchain to keep in step with the JVM -one. - -Codename One takes the JVM half of a KMP project instead. kotlinc already -compiles `commonMain` to ordinary class files for Android and the desktop. -Those classes, together with the real Compose runtime and the real -`kotlinx.coroutines`, are exactly what Codename One's translator reads. Only -the UI layer -- `androidx.compose.ui`, `foundation` and `material3` -- is -replaced, by one written over Codename One components and `Graphics`. +Compose Multiplatform runs on Kotlin/Native on iOS and links its own drawing +engine into the application. Codename One takes the JVM half of a KMP project +instead. kotlinc already compiles `commonMain` to ordinary class files for +Android and the desktop. Those classes, together with the real Compose runtime +and the real `kotlinx.coroutines`, are exactly what Codename One's translator +reads. Only the UI layer -- `androidx.compose.ui`, `foundation` and +`material3` -- is replaced, by one written over Codename One components and +`Graphics`. The same application, built from the same `commonMain` both ways and measured -on the same iPhone simulator: +on the same iPhone simulator, with Compose Multiplatform as 100%: -image::img/kmp-bench-results.svg["Executable, bundle, memory and start-up of the same Compose application built by Codename One and by Compose Multiplatform",scaledwidth=95%] +image::img/kmp-bench-results.svg["Size, memory, start-up, recomposition and scrolling of the same Compose application built by Codename One, relative to Compose Multiplatform",scaledwidth=95%] -The executable is 28% smaller, although the Codename One one still carries -the whole iOS port and the full Material icon font. Memory and start-up are -level: an empty UIKit application takes 640-750 ms to its first frame on the -same simulator, so neither side has much start-up left to lose there. The -benchmark, its method and how to run it are in `scripts/kmp-bench` in the -Codename One repository. +- **Recomposition and scrolling take about a quarter of the CPU time.** A + state change is drawn in 4.2 ms against 19.5 ms, and one step of scrolling + in 5.1 ms against 17.1 ms. Both sides keep pace with the display, so the + difference is headroom: for heavier screens, and for battery. +- **The executable is 27% smaller** (16.8 MB against 22.9 MB) and the bundle + 20% smaller, although the Codename One one carries the whole iOS port. +- **Start-up is faster**, 905 ms against 1097 ms cold and 572 ms against + 638 ms warm. +- **Memory is level**, 49 MB of physical footprint against 48 MB. + +The benchmark, its method and how to run it are in `scripts/kmp-bench` in the +Codename One repository. These are simulator numbers; they show where each +runtime spends CPU in identical code, not a device's frame rate. image::img/kmp-keynote-schedule.png["The Keynote schedule screen rendered by Compose Multiplatform (left) and by Codename One (right)",scaledwidth=70%] @@ -43,6 +46,12 @@ Codename One form next to any other component, and reaches every Codename One API directly -- push notifications, in-app purchase, the native maps, the backend -- with no `expect`/`actual` plumbing per platform. +The same build also reaches targets Compose Multiplatform's UI does not run +on: watchOS, tvOS, and Windows, Linux and macOS as native binaries with no JVM +to install. The Codename One backend runs the same Kotlin on the server, so a +data class or validation rule in `commonMain` is shared by the app and the +service that feeds it. + === Quick start Create a Codename One application (for example at diff --git a/docs/developer-guide/img/kmp-bench-results.svg b/docs/developer-guide/img/kmp-bench-results.svg index 39ff1e0ba35..943dcd7d7d5 100644 --- a/docs/developer-guide/img/kmp-bench-results.svg +++ b/docs/developer-guide/img/kmp-bench-results.svg @@ -1,31 +1,37 @@ - - - - Keynote on an iPhone 17 Pro simulator: the same commonMain built both ways - Release builds, five runs; a start-up bar sits mid-range. Smaller is better. Start-up of an empty UIKit app is 640-750 ms on the - same simulator, so start-up is near the floor for both; memory is level; the executable is where the gap is. - Executable, stripped (MB) - - 16.4 - - 22.9 - App bundle (MB) - - 27.3 - - 28.8 - Physical footprint (MB) - - 47-48 - - 48 - Start-up (ms, runs 2-5) - - 596-685 - - 646-794 - - Codename One - - Compose Multiplatform + + + Keynote on an iPhone simulator: Codename One relative to Compose Multiplatform + The dashed line is Compose Multiplatform (100%); a bar is Codename One as a share of it. Shorter is better on every row. + Executable, stripped + + 73% (16.8 MB vs 22.9 MB) + App bundle + + 80% (18.3 MB vs 22.9 MB) + Physical footprint + + 102% (49.0 MB vs 48.0 MB) + Cold start + + 82% (905 ms vs 1097 ms) + Warm start, median + + 90% (572 ms vs 638 ms) + Recomposition, median + + 22% (4.2 ms vs 19 ms) + Recomposition, p95 + + 22% (5.3 ms vs 24 ms) + Scroll work per frame, median + + 30% (5.1 ms vs 17 ms) + Scroll work per frame, p95 + + 34% (6.3 ms vs 18 ms) + Scroll frame interval, median + + 104% (35 ms vs 33 ms) + + Compose Multiplatform = 100% diff --git a/docs/developer-guide/img/kmp-keynote-schedule.png b/docs/developer-guide/img/kmp-keynote-schedule.png index 10660e0dce9aca3068dd7908d02bd20a6296b6d5..c7bbf1a4be964bbe38d5655dcee233d5f0b8cd5d 100644 GIT binary patch literal 271677 zcmcG#bx>T<*Dct1LlWE}Sg;V>-6eQ%f)g~jLvRfq5+t|;cXtm2cWqn)jW?P`o4LR5 zd-ZCnrs~zqo0)&QZ}mCd_x8Q#?7i07Ye#=jQ+S0%js*gNUMaqp)dYc1fkTv7Ff#Ba zRbwp$LIQylWu>%zR!-JEvvdYXK<7N}yEok}uUXgE(D9efTd{Z~pDYr*BewfBeZ%cpd51P5s;(;JFz9o5<#I-+ze|%uzh$ zvvVkwp+|y>gewhxvT*_7MR`v9K4pBh={Q)jI=)XUpeF^%n8MxAC}j z^Q`~ZOMDWD^6tz0n}q_RMU`+4M<`1iNef&Z^XHT=5o`c|nxFf5um(5K4!rHTT|f12 zvzz^K2nA#e2?=HfO9merK|G~!5tsd2S;y^xMN^u%sJrPkmlJu(nlTOU!0S7M*<6D{ zB&Z=Vg=UCE6wYbwZ1+Hv9+62QB5)MwdVj?hyEnL^S7-=(&$0U4P?p&Ry}g=1x_U3u zJ;i#nwVrd3)IzUgCx$L3DBS6eBZEtx8BY$0Id}%dV@A&L=xM6g;J~}9%_xbvX6Zf& z;x)TJ^)aRypW2pK(E}a^4=8wlfy|;_6&WipjLK5a{#Xe2(C!Bw4hTd#<==#Ij$$SN z`eg{YA(2B;kym)x`K4R3cZ!rmZpOM{1iLIRcK_YMsX4 zuHJ==fLE^mlr3gW!8GHX9zA`^r!bxJna0FmZ@#Xq@YKcbu<=yHM7Yqp&mp# z&7pQ;v0YuA>qI6$#q|;aS5f|p)%(8Rh`tMo-?HkI${B*6k!c%k1wwK2%k2&|i%iI_ zu;%VMl9(8?RTAl=i6-CV=5k`8BE#1L)He|U&vdhrU}G(UDcK*Pn1nU;=1*TG5RVp+ z9AibMq&rVn86YuXH7#4)*$jNXX+dLb zXj>8m*{Ccu4o5{nOaj;K8rE|9L%>fR?O(Fo3yy2#Y7?0PBy7kypgA(41Ji$qscfMZ-&69IRmX;9OGU=N^7}|}XGM@> zhHze|rB>bS43*u#UUoXqVMrh-B{NQGNZ`{wn|^EFQq={+4s;@Wv-RVf^(p3}aO7bS zC=VI(0zDF8)LCf(!A=dh{Gey+<=b3czr@W&MnYEcpdmff!-pu+ zGx>z121W`rXU%;GK`BYL&7d+A7Up?(T(92kKN8~w-w<;+aMzN^b}K>wyN1)h79u0Y z8|4K;V0-jt{UmC7DC)6p_4D~I{8_)bafzcnt=;no`}O5`0SXn7iuTn(D6>%)yvJ<2 zCgq)u(_C@9Sw5@F?mHyli(o(R6T=QC$6fh&3O>i-(3Tlz(?@mduxMiQHTzu>RwEj) zbj?|p-{$6+U30ZT+ff5qE<>(rAdoi<`5pP98Kr5raQrzhP~>#vUs4~sEVT_H^=89} z<+xaREACHK|DOLSNtyyD*I&H@qlN76|3&b^m~^z>yrH{b zikW7!{pBFtcOO6XD#&SQ@^rw7J6#2t2>G|5>CX9V zWw3fx=@^22Adu%2CfuH@p?LoHi>wB|T%B?Wdy#UghE58x|6sVgZnMhA zt?9Y|h3vz88tzG~9>+xb=gZ?q_qWjef�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

ew9*H#2CA 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�Fc#k(wCge^YdD(GR1~kA3fNk6Bq|6N{OA?d+Y1N%p0ju#b|gd_A)fO7-)Eh zUT3y%<^-i)NJ%j(a-S0jPy{tCIPSv~6_g1i8LspXo`cvR7@zu}^8u~2%#-CBN%l=P zVt<4&*XeILG!fI98DUJAW)U-!;aLm{;TnR4gCD;IN#;#h8Ojxp7^g9qh;C?9%-_9& zO(eY$t9-u*2r8|$o1dR+ig|A>0~jU3)30awa0;%P|8zSrt$7#kZ})zWGt5-UbrO<4 zgq>eZWxcxf{?_K|z)lhWE6e`m)Qq*5S267iMkiyxAjec>=f&Ybbjd@~7$fg(Y{ij9 zPVJ8EXVcZ?&-;~*j^-d&+L3BjaB^)2Oa!?Ob24eQlk7W=EtdN=uJv|5XWMd>({4!z zkZ;>{CxEQ9!3W)%0E*Ce-VJs}(yHL$>5&aRKMu3RF?r0uV|g0$-Z1-le~86T!wOB$ zVnb1QJj?wqBvTEBkxtFu-^8}ra2T*^A6N@M)gW9cc6%A z{p=Tl6+x60^qKL2R$pFRa)pd_kPeH3kN`R%}r)-Id9b(`SJ_F zQZgYsOMzJA8rEW;?or>s`^^SE>*n!$IP{T^!Js?4*ry}7o|_ERdovjy-qWJcLruWpDZC>8Vuu z5_7&R7$q+sFpSD=Tts#p`9SGhy8(yRyH&nCNu=T}#Ri#otKo@kfG%(!O1Ui;Fnfpw z8yrSJ|5EH*DiSKHJ2z{*4rvBI*4W0e>m8IUi!&ox4ApPwkioRm1Ikn->dnkJNNDb= z(yc&EDId=aWVT#?>+sv3dd!UxzV6>Y5xs)|9Bcx!x+OdsIj*D_HBWw3-aKaqd>09a zeh2*he^0EB(bH%|b!|J!c-A!GL1R_?Avx#hMpXMPO3L=@9mwegkI9*mYuB#Pin{j9 z9pKeXI}6IZqoUt>&4e3li|NmV2WWiAa}(7;Z?kX>DwsYVM%zMpqU~y_&SLVzt#Ecw#|1` zw<#%&YMj4==zXy#4L!L9*bT*>ye+&DZj;odkmUx0)|*oBpDG;&b1<$LW&vW%5M;Do zha2Gg(=t47Q_t-y(60up?E%cV@d<7iHtaOT+>;kSwYQOys>`|E*3wKYWhO@Pj{ATV z6{p5EeR{kL;Wy3nN~|w}&OZNNwB9D2F+OzlQ~)*(p%3HwfurL7q$Rr&khDS&^ z;!@HO?!5 z{+4~lmi-%X0k-5{?^kzk;Cy_(4^lM_tlqj{1-f=XSjSZ3%#OTqAVS5wTK^W zneDCY-^47O>6JEZVewat{3a|DZS()>US4JJz$sw(zeF<8-Ir{`twJE4$2&6KSD6;v z$OQ4xe#MBFI9mVgWWTIG!>w8rnQvJwO5@F(ABCcbBfHnc{Y59vDEX>f0(}q>w_^9Z zbhR!A5tekKzm9d8Bt~W2D78qN-rvuOeul}BTH9aEg|g*!Ap>&LAT}uG1{kDo3;OLD89%RQ>gPju)-1PbBP@2WDWb zC3@jas~4@2v?j}IiKW6g{9J%?!^ku6?!mk$;*S-yDDC1Oh`9fzs%k)u+NCgYRTm_R;}e1FDca^3Zo6p@SsUB~SpA{J*-HFMIFESh4i!LQTP#vhw@ zy;W37x)*-k7({{V=yJwpTPd5RG9LazPT6^Oz7tY~Y$nS*Ai4|wrGt~HX?!yu3x{5+ zX{Kas9rkBVozDni6~h$~9Ze+hkx5rKdxuu}D9vO- zBKuLQ*&zLZ!3t+bZNM#&AMR7IpcFu&&)oGmdyG`(5fW627nz`W$XAUW6jOCmA7VJ-0=5 z*^wAC;o*IpEE5VGhWxwtGvn^?ZY@>(zA1k)Rr;#Go3nBFt5xjzS;mM)pwPwtiiVpS zO|kuxc$v9nr{BOVh1W(iF$K%v#l`s*rCc6v`sM+q{_O{mAKpfVQ||OhsADKyHOj03 zm3i%k^*fK~C+Ewhb3W-_4aoe2GxQ-6$si`i@$lhJ(|I6HMQ+et3~CW~97IfPtYVs@ zh@8B9nvl~luqahk-vi1<>hR~M_7nlTAM#c1(p(Xxt0OR7tr$Bq$V|F;i&}28E)OiC z1nrRO`}Z)oe(KMZtt|!1F~IeMi?c$LODRK;oMhhn)t3AdY&Hyv5})=+*hK$LioDXxGfM_QdBG*a?<44I<1N)w%JATF_noKO zLYF(prGS|BNY`~SEzg)Vg+d zzWp6kGN3ZZ#j&_xf}zKvu4G#Tu_dGb5OCDFo*r>ea51X3Qjnk2z2VSu*QWSkT-cBG z`LtVJ+K@9TfM^yc=5Rxd%ed(!3}3;r6rj21wC$V)#KtLk)%ORK@dafB=vur0qgE^EoXR@U-_` zdnz1BD-3q{4>0>{iw{#pB3*IBl`|$68enfL*7MdFH4phraNDr!e&xC$$Mh`b18joL zpI{;Q@EKs6EO~aaOU@=zf_`TIdG7mY-HNz!u`XY}O*IMFvom4hcW_(>=zz4u_si3k zJxMc;)!;6a>CHbZ5!7mMQ5^g#)VA203T2{eOHHBVjH`0PCYsil4WuUVdtJW8mQJDa ziRZ&#oDvmZR+N|Wk(P=(2Bgu_nH`|?FKvpko4}oJ@+K@{zH`{kc1<|-yU5_&wAH0hXVhD+> zKR=oNjZ13ivu{y6vblevfxI_dh6(*wB12f#1pR1Q;yGT74gYpmi(q8=Shje2=ky|3 z9=0UYs)CBDAC_cE9H#8eb)`w{#7w^V>(ea)EUep{&C-lv@iZeSmCw8`H7F2=)7n%VN2u(`e zrW%&uiMQS_xPm4mlFL{>!*4gCme^m5XU!Em|6>MOxowj^Xh8zIiJJBIPigvkClsX8RAY}SeP%#p9Byo$Mlqg6US5{R!%h36frn@?qA_-)jtHh+;6 zYgRi#u5QEW&!@C@lXZL#SxgU$L_uE$3-q9&(KS|t099Z7(9(^TD);TIqBZR%!zeWl zzTvEY~{;=#hq%q=K*gp%2AY7TfEDBvwhCLTd_cdTuHjUN=gRP^Q)5GnEY z?RCJwfm7=W9r}P;&}~D%zQ)B}GMf7Jmw=oVEkj^E;&?0dwZV3^K{ob2ARsm0+zLXN z^)0yC>6dZy@GuAqcky=Kh*{kBhQ(%3RBqJ-NBYt|*@ub%BSa$0%N{-&pAb{oI?ut~ z!GW1O+;r%`30lal{bpD=e4t2KS!7tg*t{_Ta^B)PtRF|hXsn!jE`7H88lT zmo)U@jj7Yp-V*Orc|pb6dIb*TOjl1aRPo`*V0`$n&g4@tRX?k5Hbh|iWO#A# z^Z#({pud0p#&xUVuU}ebw$O2(Key(ldzK-)7sF6`;pqvDRaPlD1;$1tNgLgk={mR#P8xi`Mgh(bw7D%uF8k1HfO`l8UwfcQnEe0y$0yY^x z^Y6txAM%q&k3P4yLfZGok92YfL_A1#8#KOjgoaifX(NZaWhQr+aGvxE-fJ?DjZh-|l`6B&J+j3&5QS#933@nD$ ziLZ{~7-u?!}!h}X~pOsQma$yea7`v2|5B%j8q{ru6N!siAwPFN5tv<&d} zF73a;x&~85(t390ioGDXlk2&UAmfAuu22+;T)-Yn-?;$0NB;Jhy#wi831bF;@&Tv2 z0FPLTF{A{H-hjoqNlPBieQ;6-_E`vAd1a|7qoLpWn>I%|Rjhv9z%4mP$nC&e&dS+2 zXdjnlQVfCf8qhRv4#g>Abu+ye_IN)ICw6=tZ8d9GsuMDQp=)h@jBCN$%QRWZYGgrS z)@YHJ@grTm^=IX39nywtsUns{RuC|x2`cGq3ieJKFAk6Npw;Bx*JZ;ktX`m z6@1*VG2mLnfi9wxqrk8Nf+2G!U9{HV`3Dn11~v>pz4v}TfTqZHG)ria>)u|}{AaM5 z*oTMlO>7PNR{>S)^g;`Yz_2Xor_!S5zZrl)Nr9_M(u;Z-PNatHwJaCi>p0Ua)ZuE{ z9N{>;)2JZxRmzvvcMl_B5&t!$(e$ewKMlw%B1g}(^5rSaGL#h+6aWf%W!SWIk>?7j_gmSw zUqD=>@h~LfzUSX+iqv5;j(Fwz)02If zrF7o-(lG)KYiGeUg4%zM9suca4^X)qcJ~3viV(_Ld$s|E0zCiPKBj-KCF^Pg9ZZ1h zk7mPFOHF?J7wT1lIyfKb6l_K_$r}^3E*s;_A|i|r9{7!i%w>44cCsK@SKpxcuq0cD zrHbM|6PWd_T;?-ce+Qb*W2x7Wlt(J9EGwJ7*E<3he$i>An%=MLF|OUfvKSp zHf|OZi0o4pU2E%M!4Cy6{6-E5rQ8-Z8(2~>>hFEiQhbRjq@>4r-OH>taniB+Pu7wv z32%V*7Y6Y3cv%-oxnzzG3sR&x#q2U+`ak?mlN7(OWO+zOgJ^o^inrlD3iD90yDP`) zME?hJ>)V=9KR@}eDAZ!_3$4szz8ympm-X!6y zf>b98f=HaU@I(RIMlY=ql3hNPQCwxO{$EL;> zXnM1;V+it5DApjN7{~)kO0qxrCnQk<-rl(fAhH>9go7#Y^=mK(GRc~b)ozsiJA-Af znA4(yl&NWXj^bUj?+SxW6d7VZi-ncJc4G|;ckiCQZTA{Nsn5-C55gey7g*)PNY$Aa zBB_MRtcR>C+tq;w3CHM;h@XYe;}%dfw{QUy(dxCNi$NmeO6%KO0KV>oPE4;z0cH{{ zQi0{=P7O!1`7p!feW5~#I9M0w0~**VhKjW~1qJ`$THcc0hMZ0L zgmX>pWt4iZCLqytG_PL9M)+w2H~8L66({}ohG@2mGN|Qz?yOOEdk=NxqFrR7_`|Vy z8Dwl!v>x1K*UxQk6!;efvP7d>j~T$j30{RF#_l+x7A~*{YTc^`symI)>KW|1pIca- z3%?|2cM$1k2;%LfIBUKJNPr?;h#eUB0M+zu?$flYKYzH*+Sf zE)gfCW+6@1Tre8Ly=231QH+%Ly-4BrYnDQU5kaHoR%w@k3YfHsR>ySF}gbg7(C8^Lct?1fE{K&BRB6;7g#dZ1>fq7Et?$|EWidQjgW* zirNv}=3if00ZaqiMr+&uGkELs?Iu~J5QLa@qz1xW!%ps$zSJB9TUx-M4HX+Hk37?M zw+FD@H`}qCHBSxaComTUh|OwSIQd|{#%=KbHWNqQ!%Hv=8vLd9)*6M<$&@;VsTraw zIt_Zb(UO4yPODEf`y1)pfG!J6`{StRA;ye!XMBZ848IGZ4R@8Qj1lR2RM4??g=e9>{+Dd|_N=F@=0K8UjMqdmul8Aq})oVRJ%AB!NuZ1|xON|gWdCcazhYa31H+s?Ei%v7>*Mo<`)*=r71)h_ejf{fMR&jNZ)?%XgkFvA2R^vqRFjs zjLe7n=jrucG#;PZ0k$Lj_3*{!^9=O<$fzidFB31D%}In2i>3mpvPpv~F;esSzLf_)d^(l*{bHh2wp3MtI{6f2d+8mIg^j z5CSJNi^((s+BfLhnjeRdNaNvF)^d10@IIA&w>s3Fz#hoKS$}hDbw)6f=q4GNb$KT# zi-!BRsaS*r$)73@CsV1rnBZ`W_ufUlp$?FO0u|(o4QFU#Dao0G@&{$g=^~?F9b!X6 zaU$oG(}mwyU?HGpfryQP<>eQVa|Bd^DwV3eAMMeRy|qgqJDXQm#);9c3kl%GhvuP# z2^WuyEi5c7W$Pyek#WmvFC_GSy?gJ8Dr9Pm|NQhcf4B>A3&;iRvTEk$K~|SYu*-O; z|5OviIaY>M_A^6TMwM8~jZ+!#)5YAknjrbG2ka(j$c=2QH2^^$yy(QHDLG%P{)Q4W zK=)qjyyASarvsuXbBz}oDx0|(DO@vixfv^Fm17{!3k!d@>sjAS+y~wqg^Y8&{%wn{ z`<%K}?){TKw8|99Ob%WmT4W@HE>LFc3egA#tes0#K1YjOtXEFepX!RY6<1Bz4)Uf+ zDnW3vd*`p#Ry7wOqK!06gc5JQqu@rV#$J7?S*&+3Rz}vFs)}n^tTnRlFM0Xht_vG* zB=#@CJ5*^qbh4C@D<7j>y98X{1+2Q5+lEvNZz7vc z)>^O?1z^ir=odk`Yhd<#fBZu@OiQbBTSNgHDZr)#^oZ$5cC1VL9lr6Krm#Dbl6}yV zqs%QWb8@vhJD&A-JX7v}76r2!JUmvThH;R-!k<8T`?d$Ho9YdXhEO^SgI#fo%#~*C zeXx?D74x3os2y`#b&kqe1_nO-60`dKY^&J&FzZR*?&j2iFXUi%Vxd1bD1rCC6+SXg zEmY2UKFE^5a9{D+vpoGTS-qB(R4znPp?7P0RjbvqLEgMAVn<;Kc^cN%Z3 zjN$~YBr7Wq)1TWkWDxv5`5IFTpz%PqFf%m;hi#q7nT+BHOv>~Nb%*d<6qJ-MwIJYx z0%mq=*+LgUzKPA0R8PUw6do3;ps2XuZo7aQf*BDxR%U;BeZEFvmFXx-r`*yFD3>$# zL{gY5i(Ef|U2!}4_Iu*@IaZM0@s~Oh>NOBT56zSu`Yx^%6et+*Jp45X4Om}Bqe`Y! zbzhpWP~ZnJw`Q8qL!17S*JVGYVytrgnWFsqg;Zm7iLp)@5~cqI!d9r-RpmfT2sWn= zaY--S{tW*5)s?^=`$7MtC%GJ+wjK9za0c$Mo}-ZEzG)_l zyDqOEhV0&O@@W62vfK6|?7Z`R{X*&I(PT#f zX5Dy~q5wZ3f%{t&cvMgAcFg||_lWEKGp6yKj%Kgtk*rArgdw=bs|Z9=uI*#%zir1| zysHOS7VZA%sopHHPYw~K_{dZfZBMG8@_ri{Iy9Er0gcVA_ys1%Q0C7 zUPkxkuFK}R|AcL-@cI%U_amrxC3ST@6nPkB?Q82&a^3_5+f!*G-z|Aa{-}{E;OEpC zl_bGdLJVEhUW>H3g$S4(l9p}q{t5IHu*1y%OJr=`)DoqM8eEC9kVU#gyf>e_@lIDs zk*fXr&0&&Th$ocK^E5_oM7F$cs-7@JB0*aT-6Re?cHWnuz?P6NiCWT6aAY4UD-7^X zoT!s{M78HK$=TbN&~2sUsN+(FNb9Le@ccxC8#4hhI(^V`;v@)sQxJ%N?S1nluA=ZGN*IA8QT*1)U)#&8$f_~Ig+HMvALk^A^B*E8{mmfD z_fYO{Csyla!ol3%o;+_fniJ$;V~Zd%-G2u(puB(sNRZsm>h>_G;S-jB6KyeSS*~-j z=$<@hU#QE+9a7-(*0k&TzK5S(v>J*jg-N!IE!9$0$Tg^mS21JksGc#qF%R{L)X)T6 ze|~!J5-ET#q^a>0nKKx+Ee&osY@NgsGTg9LH#L>G{3&Yqtg-z^8k#mT{7hD)d9*29 zOHql+%c?k&Ymf2Q@L6#laA6PV`BHKknj7%X|IieWxBdB17hk(%ZBvG91F zr){Woa@m=HzXu6^ehxxaPdtvA4kWfpEA9<&N6u+T`+a^&n7)L}aIj$ZXJs9~HOQ#t zT;P6D>{6Jo^gVv;%C7!Ay^Dvwqw&o@yKo5pR~MYKW{LYVh9#JNvhvR%O%~!jme+3x z$NawB_u)&U(c>z^laEb}g_P#7J#&4;E&J9{*RL|#b8tOTP&L6q3J3edO@D7uxO3j? zvVXm3P(Cv9b2^J%ykRK^ypwAMjLTHla+T70`x+#e-$h2s$;o}h^7d`BjO7jnc?dAK zF%VgwKHa({uZkPyxQJ5bZPAv}3&(J{M09}o7 z$~+Jqp!Jxl_v$anw97#%AWz+fg%OJuba(R4y5N$#ka%6?cZTb4VfL6piAp>zE$!7! z9{8zklFP~-Z&hEcF2!E%DfnIvi;f&tTlS{Z?;hwOj!q`|;?yLkogAISJ<%|)KOZme zf1A~T=MESJaOmCPH`i`gazoeuEj%g0d)>6#!IhUu=^rLs=ehsa_Y7U}V$u{K1&HN~ z4T4sO4@~y=10>i}FU*wnIYzub-B*!9i;O)G6s&Sz6uUY%EM^my1tZyZ_^en@6}>8M z_xjQP!wmmFIIQbAV~e^ZgbF)yVSJ)!7*p!k>&ZR#~ff?_t2XT zRDRc1^6obiP#U3Pd|zCPu?r^!M#HxFLEodAuq?c3H_Nq`E27a-oAqwdhNMI#pXhK| zaDF|>#?5mkdLNm^^z0gOeB{>wFImhWqMOf@4afL*&q|3NOFU`o=NIqa4yv0NeN^sI zdpKGB2IJx{%F3?;akOTj9;cFzta0B~Yt%Xs@G2NJE2Mon@y(^2thdH>v&N-6N;7ru zno&la8rw%`vOfs=?6toVulbOU{^rf;h#OKmxbmUV_`Iw?VWlRLo8Nh7VtBLf!=q|2 z-?n~wy06ww0a@Z1kKDLL8K|A|B&LC{Li5PM9gG_^iHDqo&dn4Hk9Gk}6&Dk;N(ZxM%bCJHhn2ZGmG1VpHg200^_| zeAOP@yGE%>NVcP;b*@Dyfdi|;+cge$%s;+APbMuM<$1kbTc|iqx4mPLI5zRoxh5Z<55`hpDx9s}u`Yl>siteAUC+(xf}Nvp-k)3y_s2>|9NAYQ%JsB!b5cC!FpE zx&bU~IIo#IUpd>?l00W)70+oi zMTHcXe1#zhT$m1_Zx*%%hrX|jVOQq7_(FEP1Q~7Fl)>9Z1+CxRo#_)=xrpdM)R{?f zp+7jmg`Yp9o1?(2$m4vrB+e5-eDAk)1twi_7UInA_*0%K&Wvh8`$Wr8yqX!L%ckbr@NzJRRyjj7EhB@LjBNO$FHqaoJT?=j<(Wwt zeV3xVBB>t@1MV`6lm+n`kDL=|KI(AIUaz^8qs+7o1cE=A442^D0!?Nf)};_W@ehsm zAj^W;EH4axl-5&dqV9%D?NqEiYMAsr*|VzcUHZME4r{Uf-EF(6qIx>qw^~Iu=dMnj zH}3%vz>K1mO@a|;T-l3&O2m0f^P~`Q;SU`On9uR2UG*F2b}pCH-h1|I35D`;5e7#q zOV_Vo4+sc=<)+B(ALYl7QD?)w;xwYZK&NBn;`(UWtIM8J`7urf;E%StrQ94F zB`q`06cr6b{FvLfVR`UvY+V}nuBx06!7f$f%cV&WD&^+pUb~7toUd1`GiVt^v}-J7 zTip50Wvnkvd}-hPlUWw+%WdsZ$83?JF?JpcogL5k8!s!5v;6OWW0l|_gwd>U)I~hS>vverFvP}j z(k8+}ssob32p%1A@sJ$gSL4tF;Q<35)vZEGt!iEx&e~`w>iD|@08DTgRF2I#FCMy= zwMVQB`cM%_|743fIwPEl$`G^!mK3Ny%dEOR3EsxDB8yaWS69k&r|xW9rfRO!ZvK)* z@^#+TulB5%$}a}Kbh~Y(Ug2=TKQ>jOr}&iI-mP41g_BR^6OLgl-WBwdX~=ZNQdfBoOai{`38-v-|@Hc-_3E%>%= z*4}v1#hMiO`SWMVD^q*Bhj?3r7GQFLnROA48&2VM%vJMW8Io~rVOv)PV+b?Ay9v{Xo~w>xdHtMqgzSF%+a(w~m zbsV-Z_dsF#%~(PNQ>Y|HW_P2u5{}OP&?>d~#*056=@A`SY}YkwCzLkbSAySBl=>_Y zG6cW-Q~q)!AcLSl`y2@-CqqrE*;}7`JvazCSOadj(@c}drIpa z7axzT_uPMsCM?n`@4HY^AtMRve82wv$Bk6W-sGXr4zUK0+-QWH=bIYNUg=e)iMss& zj*}dJ`droCUc|_?28a1S@0+h&+Z?NPyHG|aav0(wAS_sJJo8hT^t_OW$!yTO)R7y@ z;>III)aZi^y-H8Tm0#JTu?>z3EkT|6>lI!{Lpq-J3}{muIv6v zG%8Hua`^r)>UsN*)&y3)BGtglz`}55ojSVL-pyHWjy*i^#0|FG50yS%*)qxKN%PwN z)fS(xdEl4r_eJequf1OGpiZskdU1p9%vN~-2CAKDKt*w%-0YuAe(^pn*YwaijC#mk zQ6V$k--ccuF}&+7W)C8VY*hklR($3Z6rPLI(Ouo%-Gyuc1j69jB<7lyoo=~puexZ4 zr_c1uFs(U3CFIwc$00Xpg$sbU@B#m$!OMlTQ{4ivC<1G$8KK}0; z>|9(W8p$!J?ka=lv6Ey;YSE0$$E)u^xm^dQz$QXB z{H~OGvm=3^k4wzZg3Us;(_)ft`!cLZ{ER`Qi|$W@&E-XKj91e))$#Nf(8H+RQ*OA`aL1grlXOJ6y767V;Ct z&yEJkPeiUp*NK0}Tau}>kjieFNY&1#paucVB2zhIj8zeTA|6G53hF!0LRVVAo zVJ?l^g8L?=2NZU_%7&HJgV2P^fE?v)PhEoeG-o*QBFh!=J9|vG5htlX`BVg{-*Xj& z=n+ppojHv+W)f3Kg;G2rX@%z*v>pqv=oN9?>xj?SYS5|o?5*J|U+Sab@l@!iQML|y zpZ4DCv~}CGE$mImn+N(;1uugo@oB}qhFFkr3H$v!Ds?24zk(6;UTgoG)!>hr=iR;b zxUV4}xLK{rh9#TCNk4p85ktZ$wji~dBe7c|Wo%9cr`lj6q@WSCR+FRJ!u3yV_%S+i zZ*8g=Xs)zAN12&xK}3U3c#5-XBF0md61%f+hoQbuwI}N~R99!N)#wn15@j)UiN5j4 zqnm+B4Q@=(FrXY4Fq78R7>B35Lf4``_C_fxhz_R#@2UIiui1QQ-nunEXd%D z`mjOL~;MnuOf|5EY6sa70r2@wOz$FjGL50*X3dlU>d2Mt^B(1uc z;VG2~La^6#9J*!CVzP7IrOLos{^^^brxpBl)B7yOO_^1XaWa9Kb%oF)8H9lq`tBQ} zOP&YI5Y479{5{Vk*3;kLogS^3-o5ZpqPM?$iMx2s^L?CL`TYAw{a({e0VUBT5IkfM z*@$x2kb2wFuUjEEw4XW*7iWQQtZ;z_)0~^=$C8mxZEu z7RV|v6xRLP552?;O8kj-X2a{{6W%&3F(;vmQ_`rECxQzXoV-iJ(iu|ox^M%5zVn0D ztH87eJ`cIKxTHR!Qc%_ax_dEH^yZOP^?T2`KX1*t#V<@ZmAzyls8lockGqCg7e3E3 zh0VIwK5|{r)=uU@o>G1hXxgioC}r)zH!Ig(suwd;owYUW*DkSdjvDjp>Fa`xCpd`e zn#R~7LpcAwGHf%AwVP1zTYop=9(iHj_aH#>yvUmq#BFM-H)D#&wM&efizaHzoXRF_ z-3$#I=(hM%3#VDIvsrrO5)TQ<7aK1t z0)1wVCD0AA$ilH{wBImuj#PUelY`CRS#2`N9(NpfiDdZm-%-p@dZtj!p=*9|{|J+} zgN&+a$-qRPZ~rPrjGP3i;PX*zkbeND8_J-T?8h@LE1;~lnX+pZP!LVK_fd6m;YZH( zkXTDjm;G`|VADC77-p~hH7IJRY^X#t8$D#n7wXW+=05LSGj1!8`FUip9(T$%eTn$v z*Ho*2Uz(4uSdf*BeR19xKeznbYmvd3A!c^J->FxZkE4#z7@ldZ$$Fnlhi}?(5eSSI zx7#08rCpe;UnspMce(XN;YOzUjmO;hmWaz4z$Wsn`l-R_DJSO@T#^260%F96Q5uM` z-vtUpc30asoAW=hqFz*j?$gtmkCE1z50Qqcy$6$zHZQyDU=!=pV3Xxw!!J_z+6v`0 z8%kWy<n$f<0#b2F#onriK zMPSlXFAisThUW=vIiE&Ri@I-3=kAIdR9i1(VTb>9dtsK>n_z(?#!>x7|1VOS|WKvi7T(qZiUI7wpmC>zf}%x zgwVddq~#>ymJkqWS*4&*GU>!btB|N0KjGz;dJT;}hJOs?m24+aF5271D9rET;mSz+ zYLS?Cexbu=bYns5W0iKtw(^w^Y5?kq%ZN}#$lDuVNCxH+Hg?6Sgl8O`|LHU;)IIO( z_-Rlpnw~h*O7%Q=Pushddd+p?*s33|zrJL?{7p{uv!MD~zhAuThS=0*3ZYN|`nlO& zlfrQhefM^Me_OF8;j=q00fXWou~dh|_Vw*7I#AJK9t-_CqR#~sn|8L-5=W6vEqDlo zDTi&h@LZGRXIwor6xj=!lK$3M1+Ua_;Cw&3K^l&r(kRl*bd1O4tlyI|^gaKlL)$36 zat3-OF!={3s=B*Zjm?4H%z2Fdop=1hCm1~#_HJbJBY)|*CzRbtXR@lh|?UU|9i$BiO4q!o%@p+TkE>%_=nn} zJMP|{{Tp8e>?UXb?tdP2vb=)bim^4_sU@ByxR6b5DCRel#U@W!@r(rVIGra0GoJDK zbqT(voBG135SSvPQzmMT6Kz72pRw1g^-}zFIZ*3_z};_0ur*LQdSb(_6s_0e);PG( zN)euggLs0?_cPA<#5RNEhsK(}jo02WaTS&q7N zOwMO9P?cl*Q(+9FJq3ga{6yR)KL$LG{E>zraDI1k^a$uRS8zmV2=7TSe|8UIz;S=0*|*cVz_qv~POAyJe9 z8!+EH@TJ3i`(sAuYnPCVKqsIx4I0 z-2Dfrk^m?~MQLz%#_>BGuw35_gxzND&3Encznq+3F;s>LMM=AQeL4tB`bVC(_Zj3j z=nR$k#^z>hWHVo;@5m=|)I*m5+N#NKuP&v?;BG*YNJ|niWj4lnZT(gk6|m87e9@Wp z;ll@z^E0D%=Ay)XD^|N@ZluI95qF;;xM<>UOmFA;82V0Hm-ZNy*l*9?1B=`vkDa)p zwc@=s^q!Mw7;imD&7ry9ync~*$bBF@1OcIub5%mL{l}L30!BqgH_a57-bRM`dbN(V zgb+L?p~pg@z)-_x^v%1ciW^jarV&w{!Se}e>oM{oVPzcpkEzXTbv@Lg zo@}7o;N{&b?-y4`_D1~r<=oppvRVB+V-TqZay2V;0GEqc9kiGK)F+{!I9jRmcp628 zUTIp-w@*^n_smZyoTL(bF}L_Gqvzr?%<=j~_8}lpuBS#~k)=9KuAN|AOt0qOr!TZV z5AD)d@HmYcPN7%H@HnxGQs+}l%(%y0`JEpZNPAx ze8$vx&n=gYBV2cuT%pm-_N9JSN!guRn60F$j6BXXYBawZCsr_M#JTN&2G(SXe+O;QwYGw@6>(FxIPp6K_FGU**m$#>aL?m&2$mI+ii%BiOPhXr~sr&_w^OlwtpEGX^I!?|_%fE_2#34dF zYrprF%_6{KA{5CGq-|q*(pvyW*xewOxZ-Kre@^Ria;~nfPuzo|iV5%Sx#AXdWamuE z^*^f|`v+8Af1rH;kz>Nd4V(>0HIL|9SED~vr^7c`TwMISe()<>4t?#|{X^xIabg(& zt|#A4HkmPj_G596SO)s>uCAW2&?=ucwE07A>D+FogMT{3BYJngJ=wlVydqEr;xu9k z3JR*5`u1Tq#1DC#dYGR+l}?S1+^A12Q+%e}9i|)#24XRS4EXs^u!V7h*q$9LHh4mm zuI$k7f#?Bnw3=Yi6o+v?v{ST=+=crHm6O1M6c%A&WEVOES0ymD0we?CR9d*eX3IPP zFTY5?y4>~57)D1C##W>1bjLIZx^4@5+D7G2PyCs|ynUcI|1wlSh{+@jqgD5!BjucrI5)gO5X_?fvX7roKbEVGGw2 zRT6XWN6~4Rs?BTcCrbB%=z`eFU@e9H_=>HwUUzkQ4pE-P>S(rLXc<_XIkmbc+N{RdN*;-Y_MZS z&N1Q<;)GtMiPD1Ov^AKZvNq;_D0|DOD%UUUcabh>kQAh(rMpW&1nE*b1SO=q1r#Nv zOF|S7kQV8ZZWWLYSyCb;-S1@o&pXB$<9s?F_SkD|*~`VVp69;rIp=l#E;3U-G>WW+ z0H-`iuY2{tZPk=v@K`0j=Y`a2(DUkU`#SaO?EReK>U!2N#K=G! zjQ)LV_pseu^6O3PcqX_nj!#b5`m!^uhw!eL{N|E|7{TE!suj=F$33{R$yePDOGKP5 zx;C_MD3m$;A=|M?WlQgLAm8LgP%NKtkdGe?0fktK_8-qXSQJ#Uj@~@`zDAJJK7Vu(!cQ4ZYcD>iO*@-o=?R5sn0&U*qrhdK0+=Q<_ z@=d=j%NuIUh*is6(>^}u{Q&aJ6J{N&_kp>6-WCRcy>|L1k9Oqs{ctIrj9 zXH`ZUJMrqoaw)w*=O){`v1!}!cfzY6?WWOxi8HZtu|)h5H_vU)AEcpO1!6_X+&g6r z_SqJdOMESdw77W%(veia@N!#8L{GIrg!3JKoTm8|lq2){BGdXbU{UI*Bj|1R-!?a& z)U~tCq{qCJK{t?S4lf!XOfS6TDi+k{N3B45heHPa)Ytj3OBXLlUa z-DAg+$1B!TS5`jSe$P%9$ScOg1RbXlzjMUB(@>fE1nZr0R#%#u{nKX62M8&b6pYHU*5T@oH{%&m| zdYL3chh2*2rYNt)*AK_pf!am7pT{^VA3KIjiFF%+c-#Ftls)o zD&OfEaj%n7<}9$2XYhqKN5mpX!{?}_4mRT2;jp%}!-1t`zw9o3vO3b5lY{2e__uGL z{L_P9st}T}n^d4~La(w|9_Li=+C+(xVaIscX5N}jbU6HIVT$UZFs&%9!m%gzl`(_& zLR6d~_#2(Z_12(gA4IRvIpBrhSep_Gr8Mna*V8S_U40pY^P-pU)o1irn4L8Piz!TYWbf>3<^?WA zETvfA+Q5pa`bac%>7em>f85FJWJ7CfbD~c53ocd)Zx9jYh5DO;XEH62l`%YgEr$~I zm4CfRi}6cGKhNQHJiKp$7!0}!N8#>oAOdp78{g8n*4qLUmb9_=#&pZZRgrRDKUIh@ z-Kc&&&mbc-RSF9xg01Mi?M-=2_Xn6hKG4k6D__xc&e)95kza5Z-_1M@oAbCaIiA%P z93<~z+j-q6S@Z5SQ-ozX83cbv-Lu1>?4ldKcC>ysn72!~xa1Ib*q}s@*d2iv9Q-lI zS8(LqsamzvzpQ@==QSRwVqy98!(`?t2Eft8Gc}Yc?o(nj>=x2$ZDZ44$%*`G;>!vah_h-J#W4wCR#1F*B$wM zO`_*I!?5)94I;#~>E`ME$(8XTWMwvo6>PLz0s3soP-mQ{&j%A9 zURSm?#w*E7CuMX{_?QQ;tfbP}iNE&AI<-9RgAs6Kh$e`#m7;I%N;>JsCo}D6`=5ni zMM4yrkQ(sS)VC<$AP_@Dq-e2ZVE@x;qQ}MfHAw~Ns*G!&e*dWD1T0d}S>^psh^5$^ ztG;G|#K8$f?T@RZa2I;(WvwX1Qkj%F&hAoh|D6vqpPAsRw$pQKNcwt=ov0n#{k|}r z)hsXZB^hCYqQAnPM7pWb+K@%X_LNQB4kG6-#n_PN5*qzi3fN>m^!LE zdjIh7eWGaQkVgK1jK?2RL`PQ_fj_GH+c$H)F}hDw4JJ5Ohet`;SPM!YA8svD{PtGp zPtHHN*h zlzIP}eZMFo)@_i4c>k?fc1$5aU^AfeI6#M|C~4kXa&C2XRSv@p7Z>+@?7uI2-EQ21 z*m!d9oBrc{w06Wm^C#G~cUCaJQ-+y}EQfY#cV8BZ(Q@vm$EqhB4FO-PU=mo5b zV9eW2E6C2`bpgWQ640Qd5D2zqO?7n;=iji*ecv5Tciu&f0bZkqz;h)na!zuK-DRhh zK>o5Pv8=4>H({2QiG@pHQs+AM%>(hqdMCkWw`Kn1Jb2vH$7$~)BIvT_UQf`%VKhm;*M-SU9-5e=fSISQ*WpSILQ+yMVIa#jm8sQLFdl;%7uh5Gh@W-zOg- z8DJtW=R}ZbU1!m*sxq zKgYP{G^-NiwKsiRi9o`gZ4UwwgT5L9Nz5MaN(C|i&B^44@MoeYBG0?x)*pJ)% z3*Q=oiYT zJvZh8{`_8pHfs#kuA`Dt*M!RrUA1;()kAv75iPB2Q4Y46rbl6KbMB;E?MyAqg1@q> zuY_Tkj&lXsfd_SCQM})Up8f72PU1+}tsRAcTgk(Lk<5l_9TTMo!;6p7ChdQgc2Zgq zxVpMQg+?uG_mERO_n~{oq9QI}{DnFZIrifvTOcJtX?W_ID7n90goLE5b3Ta8{9b)W z)&;%_#e1YiMr1P+Ye6j`R(3w!urndpoC;B&!?Aphfyz+f*r> z@U1wQZ^q36){y1st*mZOq%JxVT*6}npMC$9S$3A+r)+~4+npDEzsEiAM}QlO|sN-wMAgIm}2z%+8y`EPijE|KF#!+t{ve9J}p zRU||ZMQh>JDE2hRfh;)mI;#6%ZFuW{32nIdco{#(>d zC=qN!K+KT_USv3>j)lYvbFlujaLu1OpUfU4OJF>dm$R?85< z2rm7x*m-*zWD0YBRz(H?ThY#ghlxBk=365ZkBkiP#$EGNmLJiu8WcgVju|}A?bW~S z!`{GP{`I;-C}lj;Y<*JJo7AU&4ikT*u0j-Q|JstzyH{DZHxWBKuZs;zuyQE4yHZ|7 zXP)j1EG%d$YALJ_E6MqCFB-{I~_MaW^3JP%HR5~1MI~JfL z-A%wolaKtjYwRf#nO#=ajruN_Nuj`o%#edD(hs&%th>@e)9A>MH<_6MOMOZCAAfB< z^pPWiI{nvyH>6(4>*lg?@GPh#-3h4j9Ry|G#s%MD{+u>c)WHnW(sSeEtimi^83n{D zvd@92qpgD+8_>OGue)RUGKDSTB~j%=&bm4=B*vSs$#baoghPm)p4oDm}Y5d*{9E0;>Xs7MCj_h4@=)7GFHa#u3a!gviaKgB+1@-VQc=mUL|>rfpmUA~wzH?E zmcHMvGkE1%FJ#Hygj`6VB+%^rL?9mS?!PQED2=ziQ)iY>-F)NRkauXqQX#+XWw165 z=Ik@vS%RTs=38Kl$Xa=yn;Rw3)b$dN%DB|{(V#sk>GZMMDI!cWr)Hdn0h5&XE7n~t z`N8_THgVY!fnL4^C#?0e1u^LIt6yAWBf4&-Mf#Tw&soOl2UZ`fm$f*~d~{JzsBLMP z-$8Zb*2v$L2`9V_95;nWLUPzBrwe)cM3kJtB<(FYSGQ)*vL40ODjc`;(%{^Ag+%Li zsdqd&Jp}<91mb#7kNp-Uhy-HsZbT7d7!kHXGq5#^1FH@9i_uy~32g!{j{kw!_2fhX zEbTRc?On;w43Ds<+k#>J&G3~WdWDS5Nt8aAu;R;$OiLM==|Jt1v@eKW7+9$e1U4mV zYBW#1vuc3#k=wA*ePTLv)M2B!J2LkbZ*F1)KgF$E0e|+^Xoua&Fik7}^%^+AF}ecC zS}lgoHy9q<{RGk@C8|_Il6%r=Uev2p9?5`>g(%iTN0i<3+kUCp)>B`44GT*p-W@ut zH$93i3A;#QVq*&uA+7;|m2ri)5d}OQfvPC*{15oG*|c&MOifMuqN(%xyX_nqk>1@y zQk{&qRa5Txl+~NL>#-w(yWf9$xHG28(>#&q=eIlFpAvLk77dN)jl#}iXXHy9{Yb3c zH-X^)5)cq)!A6!J(LvYn(FL7%tvlw5b;uanr$~{jt3F?ExjT;tjRz`2SGI;|5cp?z zDZrhVk(EX8%Fw~GAm&yI_oMnCjkmczUZDK&nh)Snt#qV(i+~22Z{2+Hy8g4i`lN(( ze}Q8|*T;{Umfu*sNiqE!fKuu!J~z!43fL@vw?)|j{ZAo0%a5NQ_on_CD~|PD$<*l{ znrX~xezso9>bEBjU2itrFd!1pI;f{j_U2R38+A4Zn4Wm{C*u~};3TI)63IKB4zVwZ z{zakORu|q;_ZHTd>fk`=K!w+qMsc(8`ajVdi)DD!!ix*f@9M9cuwJ~K3FxM9yoe&^ zZ~W|8auWiB?#|sL50d8FI4RLHD-AfPqtJHzur0ZaupLC)b8#eEtqhd93qwA(Ri} zVev`6W8nDXL$ASN!oN(P?4V}L#g5+m9VK?sM_pepnZk&C8>qm2>TGO06a9O%c&C?c z*!WF)dY4LtHw5t9PC9=2g_1gpNZA0|Ej-zn|y(kc=L_!Sz^eDOo(k!7Xbj zwk}mM@z36SAwd0@9JIrHDdhBLu&&N$SmyAr0M8z%dZc{ahlhGkR^Jywm@Pl!;xR?m z`C=p)C4VG?$T}TA5+VAL=qfO;SB_g-6L;mHwyoqz~>ds4Mzs3`U4PKrUff?5%p`Yh(*q z9)W#xaPVvx zS^N7VZx^ycH};=~eVdqAy*M{}1mU5uTmU>*C=RRRo+9Ahagn|ccK(9cAJ}}C5MUSx%9uMZQWN>-QC>>SHU;IyW`Q*tlD-JJ&EX7 zs-1jW{bDjmYQC#Q8rf<25(SGa(M)YzQIVa3LYM>b47Vat*qQ%Ij>t7aDi~hfOqwk{ zHNfPjrYtTpG{;CaLW_Sf{9qV3_;^Sp5@*Li(zk8n4HJWulz@nc2tU7e{D;4P9o%-< z^FmtPXw>iD9}|>fWHgTVj=d)&#v*(+_g7U@p*?D6yDk=BF=fKBE727LE9c#n=(`nIWv!L`73mQ&|~^P>I@RX7eH2lr=iUSbW05 zFNyx_d~`0rFX`WabS~Eh&u9IK>~6a@Q|UtM;-0HSG$dC?Kr54Rk6QzL9<$C8LHa4T z2*T4opQvi+>39g=8bcB>19(+dHkj(SRmH==(t!wBEnPVx`MaSYhR-&0Oks?$>?NrP zBhoPs(~P^pjs*y-K}?nBD?U}WDL&;zMF=8VT1UHG$CMCotG$hn&s|sQjjt%OJo^#) zZBfH?SwRJBV*4@1mr(mP>1e3JvItMq$zT3WeD2^?h2{hIeIi66ocMPN&QkQCqDi1 z(04Rnjrv%WC7{82{a|AnOYZOdmwDzvodyGPdgYkZwI;jtjEQS4C z8fIy_Ee6u28rUMRG#rKg_^)9oNIB{yY0j};bi zbRDS^v7qM^741kG)SiWk#f4K1`lIijl@;MAk9(-B-K7CzQINpCIuWMLRcl!oW5L31 z>+DC{VV3r3in^N@_qlG%BJKUL%AN}+Q zMTxfa{w$kqgitZH5e|c*(dzXZ(1FSC>)&lG3{{CwP4)H}6DdweC<}k_(i5V4!6$E8 zhJ%P_GHv#^-v3j5|Nc1mlxc%S$M(IwS1pj=Judf2FrT-$ zvj?u069dkrNVDSaeqtiX;AkZ&S^F2uy4q78X@L2Jir;+#@*PXfTCy@TLlFvc$|@>1 zh5diOt8IrQ=t1RSO1^)PV}EG)n3y3kBV*mO0qb>(|I(i~H7{>Td4AGf!Xb~e{`ogn zChd?tFO1?!NKbtG-uV&g3yffkip2k9e$NmIx?`m9ZyL8yTSbf(3loz@%9B$_Xwbcj zPR#c&cxA!~=@`Or(6i&>T-dmWy2lL?MXRJUCa>i$xQ5Up(T8^%I~gsa;>*lhTu?8? zM2C0viyu2mKyOotT@eXqK~q(ACFG-$_%W6=E$tVvd=38be(9>@G;Ui_m~k2p9sp5Y z7Ifhk7}xlQ&*Wf2H70=|MOZ9+2vO^5$+ULszP~;1!XWTOEOf_d;{g3Kx;7|M5iG2~ zyP~O2_cwO`Mm8aGKyQ45Rjt&l%=DDIfH;w>9+p;giH=N1`6=E zvCOzRJ}L7C&7Yv-K0T9h&_1bO2LT!klOot6Xu=DI)% z_P=6u@W7BP&^iCvOM?N?%dl&|ZA1gkH-No-?!C$S`n3VDGCt1RmYFh{eOb7s_&qD= zViLmS=4#{e*Tu0A1eH~8G1;QuKiDR`T6|YW#cdX3bb9a%`k8$%a8v9?KTYL(6n`t% zyvvZ;;J*3KJLTIOSzd%_&rmOz&G+G---Y1>w~7o3JSX{?clk}ErN0ntOhX>tApkkJ zjD&Q?{E831T?^N-DvRYRxkr`jkl{_#c4(v|N3<> zGujN)4RVBs-%$JHq-KUqBxm=7h!#$$z0V84*xPj-e8LD(n1Ic4{!A zz7!Iev@-um-t#d0M>){XAn3Wc@GXKRidem_WuYRN zSd!SZLWsV7&ekb;%Uv*zVmECR6=EB9Qz#%NCYBs7n5k{sIhuUp2R&zFohWG8e|ytv z)vXb9+}TGsm!0J=<QRMbG@StHaIIEg6Hy9LmgC6Dx%VqR(%nMsU5iCzQ- ztPhIg6VMsKJ`THpvPZ4+1$wv3t*Pqj$-iK8!@^k^GJ6#rjgNyb>2~r}Vk@TEbHi}< zq}oJ%WtGqBa7!jwQB!w+rp^tNoW6&%TAmqd;~tOV!BpF=k@||y*KM0VC}_S@H5qn? zcd$ahwgh!!0qF7YC6T#A*>8x*ZV6fa9340F=*&q9+@ zhco~|z?Os15rM&4q!)sXo=9cP&B^S=L?F%8hFORIB(b$)vsmrwR|+J-t|`guuWL~g4^*4$(O7#Z<0>dr|pdEMJ{K5Itbdfe4)Uxb5D zFSuqE#ivh-g{y5T|Fvd5u;8kGs`Y6s1-lE5LN3B)pH{CaXfAzHpqukf$L?7GE{dL@ zNWai^``fz=ksrgEGIV5mlKkSI9`};0@E8x&CuPLDo`apd8y$KA*lyncKD=g#L(}kF zznEHg@^5mrM%m!AF;`Snzyr7>`k0c`>TO1fh|j<9q9On|GBAnuptQM-Yjwemo+VX( zo0vnFZOc{sdGI@GkZM80*Z2z(T9U?1HfmR~-m6p9>p<{iX_lGenfU(hZa9o@N&pJ4 zod)S48c1_YS|07q(w0?WEeh=ggN4r1#E^sBl%pHSr@bvppZSNS{twGAy zMH?HRk+M=gO)M|n9OGiu&H1qWDV#IonWQefR|cX6qBDezxb&llhj)>;tDace*sv&) zBG|qSe0mV}X3TO)lz^zOua7^;M#WZNpIpAjt~z~VT2V_`y;=HqiA4={o?nv_KGn=8 zpZ6g@BfQ3gl@Ro=7{gukZGL8cewdz=sfMhWbhyg@jcE@f3Xe^?`K`{apwuLLnTx*WAHvwc^&4 z?Yc5gMhH_rP|s;*wa-9;QJHbwRIZ|%tR~C=lJ2yKx$b?;U(@3-K)*$DgU8^LCIgco zJPfNI`>rp~rZejt!^#Kkgz)a_;mZ*Lyv*kxX}1fwOR9P*i|pc=JfF@x{>tqP zIB7O4HJ)TqyIJ$}J31SL>aMTfW#6wALU8m~maaRd#QRIt!E+vXLGUD*6|Ae4FCtRP zj6KCo((_>v7UtmSOWSZcdRVMmSo!2I%l}DpOLmI|WiI{9g9Zo}Uwb<|83k^JotLYh|@QY)xK z)+iqR#?(O@Rh9aeqL`#_f=iiA5}4Te`Q?+X>B0fKL*)XvZbtqarho)c#}f$b0<5rx z%vdq~TNVy+mGP)k?CDfmKQlTym|%XeUrr5X2G#~T0-l`s_}c7Cz9HlpFa1z}RI8&g7I2!@VcHZCDGt;C6m2}bPTwWF;c zc~v7zi$3al)xdmQD{^rfv{oAjS!J$Ou870T>b`HWZYd_VE}PkNR4tIj!e72rB8&YY z6v4+5mO9N8>{w$e=iKl*HSL4_m#nDQuK}HWc#kgNv-9`GHYb8n!KPi*#vNU@7GO$3 z{8;oYfo7A_FTgE8?758#t320^a4fsQ*tM~oiZCZa_u>efv%UWs@thq`enPOzn>bt4 zg;7eNYW+qmbkNq)WB5#)Y&PbaWB?WuW_t-I-Xv>Tc16jFsg_R5f1uIAxDY(N4C+A0 zBB^dZo5*RVN8V{ZQPC2NX}>DAk}_vM+vj9w%rUdPTr<3V?(==rhJ+`LnET8! z536Zyz2kWobf7ysNDcG`bzg0G`qh}4)bIcKQT=2E8vM~QV6b#8tB0^=gn4jT*?Ukg z$(-+vG&i{j%N!e8_JN@YxR$z=k3W^(gnCoII4R*9JUI87rT(0~@Xi>_6|LU-;(zb? z%kXfFmp@C4Zb<|#o^I7V3iuH9`4g0hdX);VRmkqJMFf_)&NsWQ(`v|kufDfpI_z1j zotK+GcTc#T*Fm@r#>iJ0i+D)5opEhu1Jc&)1n?f_?4 z%x{^K2U%EH$g>~#PAR&Gw&3QiTl*m8AIfN<2TDYz<$aJV{K(UR^JkCDL?Q&Z2Ar;z(Nsi3=9nHy`Ur%@2@(qd(phe zrx80-!54J?N8jzsnuu@Q*l-uW(jP1r764vjd2k$}|NM3q^9ZGno{ovke<*l@M3=GI z^Apb4Z@yJ#?4u%A`7glw4%>)9o~tzgO5prdtmY{u*d4P0Q%Eqn>F2v&EuJnYmd~;F z_7x9@--=C_TVV8Zn1|tdQIKQCp?DV;dN*fT8f9F5_h=;*>5BU-!U_*#6f{|O2I=PJ zUFl4~jrbn_OUe*&I@makXWDj*HAv3;{W}qtg4=WL$7_mmf$mVEcrMFxUk?$-33tfQ z~X*PaKrA`y3nQCs5tve0@fGfbb zklh}p6GE^d2$hebS#1#~PWH00cnc2B&QhPwG5-eESjqGKznZCpMttgtPz?5m6LeD5 zo4!|y2Eg-bz0IGdN=_ywQ!&rKVFEHO@-J{;waH1Gd(Bjr*>DFLVxV$+s8;O!lXy$>^%Af0KGD`{q7p)Cl+o0|#R>20npLaGuA z6U)n&VS>Pj+`*7EO zqFnCb!&l?)FXBEsg}o_)TiNzG8X7@32-lX&r%*fYG0TK>~AK~#30 zM}KlWXMWx+Q&`x`m+AZ;@@9YgaEpq*LLI?I_3S)wy(D7|QeOd0!=;m-I9`lLBUlUd zWvtXse^mY;O?UOv?~Q>?zRM*FNRX6e)Hq^Y!S{+Q7h+tT&F!8eN5CGN6a<9kpJ+*(OY z#2EoBLfhVf`Y@%g>L|#XL)wN!W zw>E7-l`H~I``YxLaa9jgTh9`S7We1A7|n3!m80q8oKT2safw#+yx{Fh)D8-gp$m`Q zWGE_1vwwJ`bVHuBv$kgb5%JEIEE-D;@gf0j=4GCR!QVrxq2H#XX@kj0aDZCii8^~8 z^pF@o((rQP}Rxemcok;OJuM8%Zs)g|(?yKw&0 zB#xR_cXr;MJ*EZ$diGOrC3I_v{0fQKvFLmielx`Ui%FP-#Yg!0wp!KM@CJD?KAm%_ zOR9)4JU&RD`GjW9;=dMV7tFhih6ca0Yk?di6lJ`dLg>6-U)jn>UC!B17-2#RmnFRO z*Wk^$A>ok^6Dn8IaMF@*?Qmek>ynvAm~V+)>7V~o!#m+ciojI$>aPTt_kZH_|L^PI zB~W?%?>|%GA=r!&Rmy|KA+Z-zh7SiIR`&n-BU-owyOQC2Sl?i>f>(tSV!*WGj9^6P z1^)9nk08eX-aV?NfnsCpk#Lr_UGw)xdVag=6&~hi-3(#MFcItP8E~&N4xtCf&ckQ% zwnX~@RmFV`8m@1xM%1r*>&z*VTRH2N#8<;h{zRws2IkoQc zJ!aMzsTYA^L|v)ceY?hXc7N)2SN@EaT2)P6utYdnF#Nil_$eYIGr!>}K|>JPsV#bv z)^@b@^fI0e@W-|=Gv9grn)nwnM8tVC%)-G%Eby!Y$^|T~qksKm$am0jn|OARNG$>x zeD42#_i8!CLTG6Gzn~hl|NF_t{YLE)K)z7pI@Z@7PB`D_k+5>C&8!NC;nL|FvoJXj z+~6rkV|$g`(~A)Imb%HHP#yue#B=9AAEwM=ffetaH9S0gb^W1%A+07q3P|}RH$6;7B#?|=Y@6SL4dxzJ{uT%$%1a~xQ*?>$JG%!D@R)0Obe%i> z6n)EE33qMd)JQ@pU&IQEFhd67H9{Kk{r6O$b!Au7KH^Xu3h<`dY$`2O0qC)5y*oP( zPnm3q_fZ{;d_vSzwH7r1>j0UJ&~d|Gy;Sk`X8Zau{)5usddu;0&SEDclqX=ZYkE}N zmCIxq1vS5!&(ac9j=(A0oY+C-cARIousB@%4?G@}(L-1KIq0(OU3<8A=Fm8HJKkN+&?vi&1L$21RKJg=L=?sm!TAjZ(utD0iY zF=zRO7TgK`9|YPMi0^)>|KS+k1#$dKH2GI6J_t>{q=X&lFt2bO9i^ELw~Ve}g}qrE zHC-ErfQ4onLerYA-9|PSg=>m>T;$iUBQU|ul&-%(z#4|UazXLJyV|eTn;z)^-IEw6 z-)-KT)r}kgd%sF=U(aids;aD7=1DaJ4A4c{0fnNR)~Mfb5>rJ_-Ll>QGZ$xQ-MEFy zk?J_tgwGr6G*oR!oihz=$pOY3p;05{N^7ROPFr zq%SWY($W(2!qs~CtptD|pH>^*(fdS7PQJKbw4DEZj3ck$Nv4<|3rzXx>2qB3`T_%` zmkW>#k%}r08eQ2h;=576;4U`IE9$h=8f#z-WzVDOcA@dw0ljz%k9|5h35j-{HXOB! zii!lflQJ`56kuNQ(DB3E_U>-g^k125Tv97eq`$npC9vRf*`QAfh_z(v$r!3< z@8dtvdjwJRl z7}jYo@ohyziw7zHO0#cg=4+=aB+MlA8*tm#+f+^2jTcG%+0X(U--@>=I6`%XrN@e_ zi@{zP#Jdy<+PR>XrBeXE_0p-EwpaxC`idhkv9SSW?lSsH3!I9%J!)?y{f;U6pFGIe z@g7$PKgOVA9cj>79Yq%8Yt0y#1&V#V%yH5WLanUSJlV=p(Yphfs)h4j6x7GUK7FSk z1n{^yDj9fo6R+g3fktTcvQl@(wHGcf~!P_O-En@rQ@xP8}`@y=l^L!91o8TgWv`zzmAUs6J@KBPHvN9~a!16R$(Q1EvenmB94!lHvI5w!wJ? zLKg!)kw~76Pbet=;z|lEh21(_h89J1IM2{dc)ak`t}^RR&ED0febzw?@UpbahZK-REdr#%6Fxq z@Dk;gW(eHQX&Woj9T9v6UUJoBF5A(Z?+sgx{T7Z(B6lFY>+gWqP@2%a&lCLo{H3N1 zi>`GDj66~XO+6uY_TJ%f4ZUh8T;>~nb~R_-ff?5;AlH8Ij}Tg`W%UH7bS@iuaOpZt zH#Ao?pw;1@H-85nIZ)q2%3ycc{Tt0EmM><<1Gmj*)-lWs^!7{I?%pvdqVzi2hHq$| z@~A7NzlGDFZ~%akQB)%N3J+dG*Oc@Q`SEOWiO!P;db-w25oLVK1GlcrzZzp4%#!jO zIO2ogSX!Zf&muda4O9=JXofK9Q~yV^;?)wLx60w34Gs?G&aBrh({F06Qbtxx<&71O z!);$7e~bgo7kpg&N1!d8@o~eqWG#>~VnoE=VSc7cyNtAr&?@z>BVks#nm*o$^?Dr@ zrAb;%|F@*4YJ$Pe7J-I9+*gp(%_IUjkKLaVDZfK(EG#`Xv#iz2hZw;mt&G8FlOlz) z^WQuSy(0me*i$LpoIKIzLr*1pbh=y>OueTqZ z5)vvJe^_g7@tXfT;u{3PKgss<99dQ~$zGj;IJ>Bd*4K)qpk-r=Ff9Y;yA)@o_4L6r zi0()g-n~707uRm#n}k%kXv~O5K-XugK|^8eU|KG<3%UvE9~x{0W#B_a9NY|@ZcWfRbR7Wu1vjz83YGove% z4?PDY$9otk^RJVeBd*Wmd8mL2;t3IRzG;lyYphjj7_g7@sChfEY)x% zn@GRUnsIKv?c}4$N=}JP_1JMYiFSnJy7;{qnu?y}o>x6?1;Z;#qSlXGMQPyufm*Jn z+C)W6ti@FEyzR(q0uf>0G+sIgY_|D;8X0O$e*Iy1su6!5yULAL|s0|K9nd3 zz_HU5zp>BP~qN{|FB> zhAEw94iSBryV@|!$p(jjDu-OZmv{<)7*Z{1< z#A=o;5;V6d)ldcgBgk5eCR%hB9cXMvZ+pOi-_G{NY-jbBg!>hN%D^mW|KFW+xG^9H zWcu^{ktE}wh`4zaH&zGn#C1o%qyWI1MYAAl5f}{94$H@Y>lRxplmYIE9PHv%$k% zazE9|PtjEmuR07z?M7&b?d(c4B6)>8U}h2E;zBKC4Cl`1biGWUnuOrNh-|=oMM%($ zUyfaPHsE7;if7ip+of?L&)D{DDk{Rc08J7WF*}k$09S1ROQmpRP0)*SAX3~!U8Rso z=KhpFNEQ5IFl^dz z_*dHJ-|y!0Q`&+k%{g=igOO+C{3Fft0tEGHCnzwaLjz%3ohuXy_NbW3P&?G5{pHy74c9TZ4@#9QB|wGA!xKK||&=tN|C2h9I2n6>Ch^&|rTF zER>x^OR}LK?8p6DM*ZfS1ArXlG}4@R$LwyfLL3A`b+z5SMlJlb)?tS~BO4DvRX7R4 zF$Kyrzb&lm*AH>-BgK4=!lpM$O^Q9o43lI-BPni}{yklUFt4F3X@d%R#f(8u$RwPZ zkqinli@jHIqGHBnR_!@E1%pEHtwZ4kMpDx+kJCCC&|6Cq2V8D+fqZW3_=Y?}4n%@r zyL-f0`EZ(`WH1f=1!P+?$dXTfowo@;1TY$4NB%I!ko%85dU>S+j6jkMD$g^EBGD>uEK?CMo}V3 z%!(=XwxPH8QPEg2HX)TS1Y*T@CuGGnTMRDOyljFxZ}#-X983iu*4puVW9Qh1Vwf-0 zyGY?%`Ezdm2GS9+{Kj;3w46d}?7%qc49CZcV3fn&h)ZVJ+38$;LB9xj69`cjg+p^9 zf<2P0^w=ah@TGydbxXrvW@nKsrQc70IpDX%?RDXgr%Mq$iqtjUYm4M1h7Yp&W)sR* z#a}@a@GE6M7++Eih4WfnQc<;UXWoW|VLCM@`)(}PUPnRYTCSXo&H(JgI^#s`Np4cd zG|pFNjT$S}cGGs7tZqsEW4w-LFDSf2Dar1nl$n*aK|d~kFf}>b)?UxKPNHSo(!zHoW|78s;K*3%uHtg zwAb7^zrW=sX=B|+we2)N;NFk~(@wlw;vF_sTn}quq?{)?9GQD`{ErM6)E#yec+>~_ z{rZC!@*8v)Jp_on*89!^Xa_;KmA8;+9sH+PGa=d?X~e}aCb~rwS*&4!dI{BtO_cAG zQ8@4aEHR^oXKu$=TU#5?(mfz|UBW4rAMfk#*|(aPEKE$CkLn+TYtG``JCLPKicP{n zdZ%|&1079X89v}$G8kn5+`?R&r{YtvLd z_1vJ~Hat2%ocG%N!7MI(vR5boS(5Qg0*5I#H5tEzwX|FgnMg=@to=AWo}10~{kr5@ z2Z@Bah~RvCu{yD3GF<06V|ek>0`Y<(J7{OGaAaNNlZ@Y{14!F?$IDnv)mKL=bl{1_ z&)f?r9EKG)Up?bwWG-7?{w`Flx3Y;YzgF>#mg@Dde}8aWL2rLit5d?`@80phVL$0v z5Cv+#PPx&a$bADM6653L*|E^!6!FXA*OQ^QYjf}YhH0gM;5`jpXMCz*7a+4qk$OE{ z{QY|RixVy$o}|y8$8ZIqZ2*dCOoUZ;R9#lQq}6iNR`cnQOzS0>zf8*xL+{h+78>%& zj03TF1r}h*$#uv8P+?+WN#-$-<`m@Iut~2QUhr(l+%#4j36{><95_59FRS0DLmrz|M3ZdN6dT#b{wA51vXrw(;sGQqL{dw6*G zizXp==cbctpu3lyGdf$%-8wy;y(BrSr73-jaUWX+&EgyBD{hVc(cJWuNeoy6&|-6B zxKFHJ$n7|l^(rBHobIg1vYQi^ng#zKn$9{ds{QTSLw6}DNC|>8A}P`#DUE=1C?MTA zNSB}pA|WYVQi8N1prnAbgmic3dvkuzd;ao+M;K=I{_Z>0y4GHpWV{1Q5CeKLu4#Fd zhIC=UHqSc=?3HBo0*#Xn{VH4lI=dOFlfu#!Ot{~IFo~!|x=lTn>r}BJl-_H-{Qaf> zC0C86dIIhOdxP+D&j?ln7d9&9ymK93RbY^8SZ4GHKq=PA>N(Cipe)%FO;1nn+w-lw z+auR3SZ5wa7@2iTMr;{ zjB6bkL`3Qo=Gw&wn=ucl`Ct5|XXW{wwAI>8?9GgmNg15 zt0=_bFj!GO%F!bI0V5v+JDulEgYXdH=$$m8Ioa@o)gWxC(P*mPs(Lhk@%D zqvu~6IJ=;FVN!_mBo1KBV7t{Zy%ZV8L{F$yCW)OH?@Ys@FVv$1$-hH33o^eOekkkG z$9Kksdu`WhsSFnDuyZpEnSZ!E`>r_ytP$ZFO+b!R?9?SX)3CXceSV6r!VEWR7OQ^A z@OK75E7JS3EgM>{hTIh|f{FFscW}`y?t7YR6X?Q6{LQqu=_vy)?+%6Z^yQXGaDx#Btkd3E>W#R`@@_ve{AW)z`$sq?JHBp@6qfk|ff4lRx!ypBdwh?7 zQ?*wbJBiVWren|dt!(S{y4Ta83zl;S1A()77$eNVwR^50Etveug}UdTgqngG^w3xm zTOrPZ*4VRvD*8%`L+$0u{ZZa65-CQ;@0W3r>D#O>2x3HtEyXJhor7xt=%oc%M$YNj zQ#H0@965{z zf9+AEESI{B$>!#0@%M^JkEReTUJUl%y<4~QBz|HQw=~`tpBcn5KKjOIStvdyZbbcn zmG$R?<}0yjjLV~5EQz3f(izW;?h8nPqugGyLTy1-JI}Vxi=PkEL}1Y%%Q`M%jW=Vl zx9f>~wo#hT2IXu2fODq$-2&Ijf4fcSn6AOm+iNI2%@?G@;&K05*rHv2 zS8a)Xc85c4YqLa2P3Gimh$9j!7SltKMs)tg_IA$cCe?;6vsZu#kx^I})dta2%y*Zs z9`4Ora%i-<4j3It+U0bY9F@O~-p08jSLv7-Kx*Loe?PGs;mK6Um%#M)9Vk>#CzZ6d zTPewW*&i^0#TM}vsS#np#-bxk(VFuu~`>}Zl`CY zLAC%1UPi5(rhHz!e#s4!?Yipv;WuFTzPL*;rZu~_x5un-Lw1M34iWrjVnYDG)vEHs zxC%)u`}SRnnuIpL73%-(p)p(TXn(;@;537fADF8u)+K9Y$-GSD`gn)2l`J-r=l%pa z7?fU)?Os3U(NNJ=N6M!2P0?C^<78UZkVHGZqE*0wl#>gcc9=PE8%D-*kdJ-U%!^|- zZ}5TX9fQv`PAR*ik%<38W@a`IQNQ0hw2 zFvb|C9xF_KHl17O(gndHSPGtrvJIqCkQNFX^5CfxyW3CuY|lu_s>2gvsA9n!yda=q zz#&%aZCh2Fb!Bk_k}sO3p(b4mlm&bg7|1{bcD{9c!fcW$oaK z>lfRR;XfS_roNRnjj+kyTU2rFVO`EX3DELz(Uk-_)7r?i|Jg@>?j!4=+>i;ctK`}c zl{-@5ZFf11c84Nv=H;qfJSLTGWepLZ^CGLvG-&B1=(!Z`0lXawargQ7;P;vY0T_o-1Jj*5 zAD4I;WZs+<>Au4xymj6hJ8_P70nOP(dy6E_PDEBW#RH9gH$!4{MHGf$;r(9?CtI&+ zeeUq948>cf`(1*k%l_|UGW01J|B|bI6gxYm4kElAxkt2Z&}=&QhC$jPcal_QvtO=V zQ_+hAwk;?9@ddC%BxNRKnBVav=hhQBoBIkV>3OfTrE6Oj6`xg_xQFT+VLts(&y~LTt<9Wt%CQUY>_gW}l?@`gwGRd!_pp zvrd5(FG)<7Fq>KFJrqX%qR$UM#kTjQh_4Olp84&xB?2AhC%NRz32Z|X&l>9>)?PM3 z5I=~bHm6HW=}F&*htG$B)YF?TJ~A=_Ng+?%dJu>)##Ax4!^%D}B%lZi_*jq+E-o%X zb2y~S9!vX(%OJ#f2ybiX9+1;Pvv^@<96K^t^!$m@yb8vj{Y_wajuv?WZ+36<1IeEF z$0*)3ab%|6z?1kB@S=-feUD1yGQ1&c^gcuyi-;S@TX2iGh$RWEwC-r;`hG}FZ8(|W zYb9Wm$NtummZf>`XO5c!u-mf$B07pp%4Wbb(hUVy}nHekX7 z4O2r3#05Y1!YR~zr9|7GwaO8^Lgf50d&{KYD)KQh3#2FX+aaH%5I@b{lFF*{6TmnR zU2;aK_Ze;!$E{FxVr%ni1)2^>6~vJIJpKvwnF*ysDRz$_ zj)MZ(DRt=Xq#bPlJ+@B$ymkYq7Fws4(jp;(j2d3L!(i^gi3>$ssS$SN2zw>RjJd8VNZcu$l`4=5dxB-+X z_>yLvW{H3>3(FNO33`(G3)FIy{ofnx#l_qf0oRS)MAL1C%G)TQGD*%w4PcF2si}Z` z@6j)IDWmt*y{NdZOs4H8(}es^U8SV5sPZeJt)in#2M1j7*ND~oFj^udru^)87Bk)7 zAFa{3bFAa-U74!_vt06<)Fqx|2n)G4)3s^tpp5Lj{ZRHt@Pd_)G$R_kPT%?E{!gFQ zVE2WgG6+U`Q{rHWp=gL4EzlIRnf~&6lQ}~=X&{G_9FGB=^=C!=2ge2I$v}#_hqwEy z%Q1nKdNNnLt5N(S-k*K#-<=h@0oWVErwVX10F(@d%9UfovSa(`h+Q*R`PpC7L2Cca zbzvm3`9^tpVaMmE^X46@@S7gzB%U7nm-o#pXx`_mM1O^a2zv9@p}Fxqid)C8veYk3 z)fm}$1IL82eM1E;qk^ON!@*8U&~#^S?<{~yYWa%<_W@IMFoM?9{EU1H`V6GQjO*Q1h`pph@L0MQMb5EyF{|Wvy6*$jkZHRUby8WWaHc&h zB3kc_>JbpCK6LN?@jT7za>2i>)S!OF{^mj{1sh8yXfX2&MPAIT1;@eW#D&oe6A`p? z<;(xG!eZ}TiTkhBH^qbnj;TxOlf~DTm_;w(&s;8s{3#~i$3$UK^gy8 zejZZBd5M0Fyd#i;C9H~Dq_8r9K1f;;yWB2L4`$2%Ap&^wSaIoRyo`v;bFD*7VH&MV z*2)tNOcn6XX{eJn(hixCX=-Zn@l|%lb6JL-X}*5Xrg!6xFaL^uh@vF{0heI|B`xjd z)|SH;OE7#fo0$QYuF&h3-(16%B6rgnK`@{wsv)Ogs8}5h(LlTPQO%QB#ut8kLqh|Y z^V9+9pp|E-m^kcq=fvANLPMQ4ad-aRm7@n zjGUwB_~*?DD!%6j8xwhEInEDjwvvRL;8<_~6>&aF7He)!4p5s^5_@m+s~9&}tn5?q zfdVWLfrBdObJ$@!TnLoBHvU!@$@gt?fZ*s@+~x{CSR5dhHNQ{g zz?OoOV*HCWvx0`CRQ$=6{OGfH#B2)R8=g?4ih2!1^8ic(Y)H04g$ro7#1y5!atl|3 zk64CaWo2;0WuIq8?!!tmeawSOL1S*TpQzZVuIQDP9e-Ktwgl#;z1=-nROr{(XlGlI z#mY+~yYqRN%M3i7lUJhZ?_^;qScS7O`?R}(VNSO>}l?ouH4}6KwZd+YZbY=@%#a5-XMm=Z9+O3=y|X! z^z8y!h4%JPUgfTwa3#jJhAn+AO=)9(8(a3odDjg>0MgjwaL;-A9uk_Djo99tml9#b zxSrW#Gt^JoD+aPwOxTI`0ZFhj_CAA59MfJwB(Z0s5!DY+)<8b8q?}xCn+=tqm2yM{ z?};95e>>z1>@}EF>;LP0K^TU{0J$>${1S~nIf&^`i%Mj1V8L#;(5Zdzo}h(Z6UeY@ zHVw_q&44ht)&4quW@ZMSzTlRqq}YfhViv9;40&{f1XxJxLE_@Mylq~RQacIB zEg3U2yxG&$X9oD(0Vvq!iUoj4wrZ>6oVNQYV5j(6M0>YVGzQTH2`zR41|y|Uj?tgm zjuef3b&)UX$t_G~!do1cnHJ+tDATxw*xlWO3fOVYO3q^Ecd(K!M8<)CuMv~biwpr% zk(16Mw5CfNOE~F^3H3;2v^Cd;3ogb}$`BGoI-F{F9vbQe(DF zDN!zlh7emH$Wv!GSvrgrIgB%-J4$%?uCKlqR}hDg#P1UW1?pIZKiVZ28kp}%Z7XN#bNmA zfo;8^$GyNBysiwNwc)+s-{0NV2wF3MAjAq(6Hk%?qwI`~rc!D^LX|ZO1Q5@)e5>U( z?*MM7T&0yt0HV(+7*v&k9LPDM$AU4pW<)*U^U29c63xzGnrW#eBK#*ukTaO^#F+MTB^Yu3qD zO=wWAq2HFtf%WEi<@2q*z9ZP)Sb=XD&Lk_BpS_}G79~!&(>lQ#3qCwFZ|b*n2tf<`cWB}2Eb+n z((f%A8hk`x0RQW4(OgneS&RT`LHiGG191IIOTlz5iXI#B=KXuC>Zg`bG<0-ZOIvg# zw7F|?X0w*Tj<9Z~>3}RL`xk4s7KG3dky)m2+NnM_@|Usr0f>r*zkkizB87c@A82z{ zgs^v>$K|ANKqvYnk&udQbBdmImn6$UZ6@^s%@#-OR(p$Q{F-MU@W z@_^H|aa#s5t%50Wa1wEN;hFt5F>%THYnBq}O?0F8kj%N)!>rIydNY`(J%jbuS(GJc z&bPzx16o=nCVB4P=ew7S$q+vf-L0$;hl+;!4WYvKJzEOS{A!nusj3vg?CeOv7f)-R zOkYZVlZ^Y}^Qh3CQrvDrSG3Sm*_E`Pp=H|meNJ>Veq_EWaSIb4dsh)FXvQU8Ejhh- zu{~6zU{n@JoC{1w|Gh;{a11Ccl9lOfr^;eeiQLmJ)PnPjS>gT#<13U`p_e>EQY8HJ zJUkM_$MAAAZ$w&q%jM?U!r#RQ&^wNlR1qT%-C{>rrLGKBs8sgA)m0Lzo zLySF72dVAI-Cytm;s+p&HWHg5n9+M}m=ghoYTi7B2OF_esA_h0wn2>zbJu+gKtVly znr|w~YJtu0X~kROjWIY}V11gHkZ>D3%S;bGBsxNw0W$BoLr+)=!@qQUO%JgKo<!vuJ*AQdAI*iP!$ksNE_08}Gfhn~-1PFbX|ymQ8U~-Y zA}PB5_KKlDe{aIt>!^p2@b}xaHA0dnjI50sRBDyNPg17DzTotloJUAz(u{wx1NaWW zI>tIoJr+HA04zN0>+xq|L{mdcTS-wdS;P)varh4}t7-tv?044qdE?Ab9Ev5+gk7>u zkM-YE{~SMveZ2kfaDBXSV!;{AyG^zuEHLGjV&&&|1GWa-B|!Xs0izIrcMM6bR)5XO z@NrG-TBCjeHsIZ<^uvYuc{!PoyGDv25ZtS_{q^meBtU7(vfDa>~s*+QGS zvx6g0U!wQ!ZuQz9a?bN7(_03WR(9|1zCKzXOL_P1CONr?#m^Z~0p=GP%@k6gGd$$m zs^9to5sY;z9UY=xh1cyv$4t7+qbUg2h3m}n7Rx~p=2gf8)M;im79w+$^P^S_? z%yjz?nW91l#qUQcHOOW+pS|JN>E5M2LAh>VB~=Q4lCPO3Ku^rP19fFtGw+L)ILoXb z?2)%%^$pN4CntGg`XX&PQ8C}+@JTCn!@BrF$`4_yLxn6X#?9XRzh>_|y*+!dc2BqT zS%FS98~LW;ZP&q+RZY%i8z@k`~6?MNEwow4Gd+2u>&AOY#bWMl^avf#pE1nl@D72tm_-h}V zyCM^G-?DRb;}E1}^t1*XO;y(8Lj>43JCcep(ef1-g_fYr4jLvn}{mFnCC` zNV9{9DUz0^!|TyF%qGF3O+re#yps>%gcTN)zrSd9HI+4(l!qt79xvCZ!F6RJ@yvbw zw|!3`!Cd>3jCeWnD7e2;k}4OM>$tjRVOYx2Al^TB2suxcPJ@dOME&48y1{3?76*Kt z+?u)jB?c?m9np_KJgAl5PQhRFTF=wixPFxvKnkWc6Oa(8Q8s;x&wIT;yQ4FK^IdA{ z`P4Em1=kO}KzK{wZ26blliYCbY#gli&AfBL`L?ik6&QxiI~td^wgdqh37Gz^Cc$fX z31Hh@PrIoM2RmY@o2~b1+9D}jL=11TAc10IG$Wxo1aQ^Fo7_aS=j}6sSl|sZsNPLs zr0#79(A2P>Us%vCGy1st2cNl(^6Yr@jL7$^-e9`~gRt;PW4}3ayhdE4&eO(be7SrM zY6RQzsHHw*&!x$J(DO#WejW4Vu`k$XJ7Vc>+MR>)6M6iv16G+aw0kkE;UNCm_iU)q z4bg}$&&nUn>w8w|Zs@AzB(;JCp4wI-@y?2tffy=$lZS}rnwe;F%Jq>Pr99w(=zuQ9 zKU>5{VB_jv7@aZocA-PSWYSvsnZCxL2E?a1vHLyUBUf>D*#%Y$6)EYpSzLu5J5ya< z9bBUvx?hS*Z%RQjxBu);hj{9J%eJNID*KVy<5EpqowAMLMq3$+{H_&Q>|{ZQy4j*K ztM1g3-uxP2!YApxt^XXWAOrb`+->vvR@B%s)cMw$4r>;1U^RZ)*;c1SMezOmGjUexXe51xw zP@|y-+YA>=68zT6hEQ3=n>TNc_ca?^qAS{dmrI}hyCc{8LAX-4kHy8=8ID9MGBWLE z$w!k){srz~dNy|$*<9zt|H1WQwKA~4eY93$YjGJJ0tQ)#rh04HnV_if_1}RvJR5M0 ziey9Y2bG26T6V_Us5MW;<#fSa4&(Z-=1ZVvcPCoDDvT|=?Mff~v}D+gS^zsxEz|6z z!~iMey}t}B{HHIo*r$DT@yW0rdTe~rFL@$5#Z3Pvku`uB@VszVjXZSUEY@ts^f7{U zPu|xn5~&~bfbUv&uOxjUXSKLKM)z>xZ;Oy7!n`F5V=r4#Zkcu* z*|G+8Hb_r9H%!!Uv*>?xbZ{SA6cpy~;NSqkX5-8`eI`7>%Y}Slae-66mcHs~#_XT7 zeJ`(zQv&JG25ZVs&I@dh(pGUr<`+f&sR~k=y#Yh8=%ptMlhmM+3V=2|{AMFY@3;Bg z_Mb*xwZbZ+Gm%|4TkZe^Gi~u~xk9fKPxr@a9FRqaJy#u5dL4S&4Fu;&!fr10XzxP| zy;nBv)zsdo@e<>sC*05?y_cgcg^{2olOqWTn|hZq!!Jj}zmkq(D~r1-cKK$=+?tTv zda6CCUypi4`}m$9q{tOk2lG5a*dh?0VX>~H%T-ipym~eo*5L=BX$YKOO(L;0KxP;D6zN@zkNMGa>j52$$RABfB5} zqI%%DeI(9N_OxJ@yLr77awDeme9*8gZ+6l?^p76!_0W_mU{U~6*;Kv5^ypE?AKPbm zh$-arS&wPtbgk1Jg}BWa4OpWnqa0701e2yu)qC{qM@JV7=002!G2|MVi}hIRzi?_R za-w1JsS<1Sf!f1iiozIq@j5I2tvbn8<3kZvA+^JMl;%Z4rxw#J`IwQZNWUenj~W9S zOT8&aJp(yhHAP-gR(;|Vy4tQWt;xAzp_t8ky#_Z~{CP}Ty0GuSKkS{zX1sI9SBz|0 zTjS{HSEmPdHHMshTb|q2&T+}U0VGcE6_qX4KPy%J`0#LLP;EL7FG`|U$0-jLShgL(0*^Ge|{VR&LVihQ)Mso&Ck z0?Y!*Lz^o;$~B@!*~}iQJ6xp}T3Sp}`L4d!+Cv0p;N^~2QSRSD|Y+ znm_eBQ<|LPX|3IayVPT#2+v5O1qB6m$KSSk{l#oHjzsMv6jie@*aF0xGOK2;byTRI z_Yur)Y4QL?Al<0(J-6{re@8lfFx<^kB?h{(!+2QjIDv26*tZgm-A4)xAJYHq zdw1tfaDMD5mJm>j0U|OLUA2xtB46ixH{qd;eiTm3sP|)LCIfKl+S<&&7`J^3G@LN| zL37xA)gq}f*z+3ZRtlt9tlsdXbl)1|Gwc5XPym^n<1% z{eDj1r2Qke(+6QYfgyNqDv3LDt|M-?RO;3JyGm)Ex(y6GwcsIq}7>zmS< zuunk^_`0`fU3gbP12!@tQ4|(>m>DRSm!VSH2ZUjsQW2B(BM*YG;La$~iRSIc#sK|< z`kxHPDE6J}?h;8<40Z?&lwp>c03iMsKggWT**V1}v7T|sCeIzl9M{QH zI>5iMVuRx=5#(Lq#(^5<8_g?9{9MHOFTHf)?|a|3O{MAl_TN_hsrs4!Gx&ZEzY5Eb z)PokDWxR_t>#?kp>a`JQfWKB)+6hEG(NS3e+4Zj>T&1x10bL{7-=%gF&|bs!_?|Q6 z@x6XGG{~xov#j>6W=lsNuE3;&H3xW;1h~80I;U4?Hu&wJVuxl608L={R1P9wXfZu+ zUn_Il{38s(k&KL@3=H3R>^t%q)JPMKAHVV~{%Nz+yFtzW((_;yMBku;FgMHU<{+jt zZ>1L~a@$|7>4W|+EsgdjwWf&JCT*|y(R1GSpNQ_N%3yaFpHP~r3}Q74Jbt?{Gh?FC zMG?Y31R*M1J;?%Jz4&BFLqNTz&k?w;Ng_wnp^|o<$%-8*zr=%y&>qUp&T2Y!P6GiK z8#Q7dT2<@m%trWYUC1IhF_}G{pHCSZ69%Eyzkiu^s;-8*7L8kjPY!&uuz(81(3UM| zJ6vVea|`@&bx>A(Z~fjLOYH;np2V)(w{G1aBP(UAd*7VzQdkHMoF=;o9VnbDCvJmP zZMn{r@{{9FECw0k-~vsSGvSK|+T5d5YGfNjugc~Z7Zu|yK2st<6FGAi!IE{~QV~Kp zjyi{m5{eRPsADLMd@+f;PdtsrOAO$x)YD5k5sRcWkIqy6_{C$Z>GWW=J%J5i*F&}= z{yh%n?;|ov87!*xg$N<-s#+gdA^4LB8CU_X@4JvcBf=(?RRH3qpnuiupjTQ4rG?&t za160o&BV={H+ww)b|$ccc80uADtzLT=xw2A&YxkNF!Grw|Bh%HlVaTT6 ztr2b#f%=}lzSyKIs_ToqdZ04Ylv3_%1^f# z2+ac3BbPywYx$#le_ev#jARSZ#1v|?qo7K2-zR)LLV(DY3aJQZSK2=>CM5EHL`Xou zo0uAS2Q1)1^cnQrX%QpY$0sKkSXgSfh29-lq*Nq81wKcYBsMe4a=REANMJTI$Ih%P zEh@wffG>egYS>Mm2Q_{GJfYF$WA<@Znz zk>b#_a`;rRNb?T?nz`~ldRk}u1t`rMpKPT)c{GBD5K9x8D5j&KrKP2$to4|_X7&jO zy*%?5-alE|lY3304%Q72CRD$e;$XT@jQHYkIrf{N(ro6oeJbQT6c#SxZKB3==c@JmX>TiQlp!~Op10t+~QR?6w*jd5dG z$g#MH#**B}Z(V%jEw=LmhW<&9o^QVM^VZ{dZy?Ageqy1cqjUWvK*>;+`o7p>0CYfF z<1e2CpFDD127dNq3s)J?tb+YE?frXN66)l4n_}MkFJvraEMC4iuo=<;aDe;46Oe_E z);bLW+o+2v{+{PU7vquNuWi8cSfGc4h@lno+BgLxh!l3pjsIO`UUUdwv7Dz!vc1Qz z?)Z@CzM1wm`F7x89L)B~G;+tdHoG zU)4Jr3TaTY<90Fs{rfS* zL-=2wEa@6ijn*n;Qwzq`*i8yp{9F_5vrg90@~VBLM<&!xEcHNz9Xo%B-*D&;9Augq zgLz0&3ObL4fra&T8kfbdb7lxLVwFJ|_GagkWH2ngz9k%($fQ`XKBQk`pAeT^0fjpq z@*GTktjzq{gH^i5e${3nyUDSxn}3|Pru?>|{may|G2v{jcl?!560m`WS}2X*_Dh>hi3W24_|K`5l4_Q8z(uJF>A?yK(^MBm z@GC!1F=)Q*fEvBpZZKIJ3-IsXG`V&zs0e65i~Qk|ZQc@vJ3!dflu-2R?|k-^(dq3) z#Nz1o{Y!r^CHL{3J(^3$!o#vdWosJQb}NS?dO(sT zh52?}h7I5D54F~{z-jZBnIQTy^Y$KwcCgH-esO6@x9Qne{c0P=VAf2onl*40IypIk zm71mlILR_{+bDURA0mGKdDcoXa&YCv$QKn@Om~k57S-l3)-(XPegDeE>GQ+xu_}%C%pbcRP*vK8l;* z$H@x0@BaZu$|Kq*S{4?slleR2Blf(s5yZ0pV6z5&ah?BM9UueXjDrMsyJ3olUaNmr zwQV&tG^GQB9=g6TF45O+<$<37&g6CX6#8u^ryRLx%9Vo?v1DG(M-Umn$IeyyX~?Fh zyZc#_*#fu>(IxYNt_<&7cOa%R3Men99_zna+G<3n&-Anv*F;cWC7|DVNdw)A=P?;# zkeCZQ(4*%ir+MgT(;$E1cDfH3FrAkmVY!?&Ys3Y6Y*Fd-H70t zYA|kO$6*jiK}SWPv`CP?vCJ{qtITK(Y`GI}Toa|iroaes40Wu#@qrxdIw@5)3w3>B zR}I#KcO!6cfDSX!WWH2X*6_DL6U$H#{gtNnM`AM_E=@8Zug8Q1s@?|`PCjZ&K#m)@X(<-#WPvwf(X79(Oxsino#^{TpyZGGYU2 zt5A`*J@>g4NlmxeBZNa@$S-m2<91{7*9b(*QrDHkH@X_1$9VI*fxRC_{(hvWpD_#Y zePyIQ#HJlrs8eLd>Opy*r`@PbeqiMxvdJf?u5Y0 zzw?fx(==km>e->Np8RL9IFq89mW#%zy?o*P5!`s |QUjr6CKX}GeFKznms2983 zn27vsEOO2py_rPLZR>Q|iGo6j?stteS_U$+_dP*YMSBX)OI|0iL{HKCj; z*}ZnoWWnJF-}fl#ksTS^{71ARAY~;*{HJG?XF&g8jx8Esfls8hm>c5G=W5vO;}HDK zga_k4^hxwAhC&gV5N*H;Vw=dtjALEw2mUz-X-%C?^u<6B%X7Ia5{%;dIA8TVFnvK~qNEX5xzq%nfBE|JHkK^#Y6U(sPl! z-h)oW^9Rp9giZi)WrQah>Manj*mlNSOZ@A%|JY!^AijhrBUzQ<-Z$_yTS6PfW!>io zeCm$KkI3z2Ih2g}@zjCWMfWB=UN*n=mDV1DTHM=hRl7i6VKBUO6{q6g?#Qm$kZIAgzm?aX{YIOF^g-j1gD^d;4ANp9oCw6NW8d-VQBB|}+`TRA zygIg&UIHeM;wM(>Ix6uy#L9|Ute=>s^=lp90zQh+_vEepZ55On!;bgV!wzYpmZyhy z4=+-CGo`0qjFcyM>E_9gmzZq86%v;Fx#{pTqdRHLzCzK}m8qUM5&xbZTP1QTvH+Q(Xn_8YM@k1E_Z>fqya z^ZitkMI1ACY1`itilwL{_ayf2UWZ9r>uMlq-`k~`F2qT#B_bq?Rh@SK6qFHoak2HU{v{T3rp?L}{9#j2w9h=6p!Jy3tG}UmdlkUc zhybs$6|_Br8#d)wI1CLT4}JHyZKaJ2Pd79qe+0uTAdS6zyd*E+@V8;63j;hCR_vtt zUfE-S|G~~Qd-#%e{#y2D=3@ze*rX*oxu_wQ^2BkNXxeyf8Z=U3++ncY_-O0u`PyO& zxj%*rdSt`gBicY>aP4NXPF;)!_N^T?@CP~3DJUrox@A8*agHAdEE8-WUDANheu9M# z`6pZ6BVCDqi3&t0U2HnV8EdC7CcUhLs0Df>R&|nQ-l+Fw()>xxnEx55*`4Ah* zo}+{uCt}8>pn^jw&VuMOrL(_clP^KU9MAXU(yiCG9>3s-)P6yVy!h4Yvqaj8%T^Q0ZJ}uS z=QD$F)QYTgEEkUZm+z|tvgi?NWh1)tVWGqt+SQ{3yr4{dfM}sPGXCOw2mc42088Om z=wKtZ`S&Uhc%mKO#Ap_Z=IxlAJ_x>=_=w~QYr5E+fV%Fo?`Z2p%i~Fm@+(G(P*bhP zY)7*J6vkA?olmHd-cX(j#oWOx7yjh3#07$y=VymokgtM+Lujqm{0;BIC_Lizt{0i6 z2Hivd^-Hv~Jk@0HnVDBl#LwbUFIJ<<6x$LQe>nPxrgxt^x3&`qYTA!)92P2Qr~4d! zO|CBVdW4(vXR)gL%K4uzUbE+7!BX1lM8{eji4=MLaogXrrpv{(5zOY{OqcpUO4bnI z=LH__jFxLLq1(%mHk<0+QwyEPp1rtzdiZuJWD9o!IFXLduZrJEPWLB;7njUgh)KV!opb&LJTo0NtDgG#4&5zS@G>{g{*pyc&mzgSJHDR56$FwF4js3)=nN zhyTO*RM;y9mb%?PYvjR67#tE@^0eFxc9$UUyf8V(Y9l+!6sUJS{#`zn-Ye!i4@5aa zXyOLzdZI3jU4=H13(*jLE_L?40=84h)kS3xW{W2UqrP;bXZ%#u($a#Z{@Kx*L%HIj^yaV#`deKxyUpmYz_U|SP;nCZNU$pC>C_VCA1&cVK#RRW*I9ft4WfR3!>Mf5 zoA&j1tqs6t&?OaSnW5a*tV~B{PLChhb|rA?G8c4$sA`crwb^@P6(~Zk>!V!U9YI1u z$baYH0>8!oBCyuChORq%kD}LMtf^f7h!z_yM@v(C^0x|u_K=p*%)OlOAkgm;Pz!#j zC&;xxE^lYvu6Oqp+uGx|9f5^XR9vmYRTG56)=zH28q)LceYp>F{Xf+mR6Yt+yZWw- zSKBVlFLGRZ1M7K>ob%~uzLT^^+Q?=Vt&nHFPb@7M^*9ZxqQk?5Tn^Xk&8H^6+Aa;0 zH9L*o2HHO!D49l6-!n%g9wG~xS;Jv!toZn`5TLu3d9#6{crlij9_dbD`g^39sgVw%2kYPyQk&C%ryTjS8 zwLTIw_c@D|yYYM%NIfuheAcLc01{6sx}MQm>$y1DsGD7P*~Ahy(0FK=9HlI^Fz)sBHC%#ltqk=7HYq94LR>X)3x7nS#-#$Fy^=} z3h~c-DtY^yzyc1`TA=mdSl-Cjk?EHVq}M7|E0sE_pa~O>+4J^uXuLQCv}Ajep!?cl zPZW9g^cTCXwX#P4`Q^93;UQ7%SvCl3Wl$K16~8zhTYqx2HP&o%QaC#LG}!c1o^QC+ zV4_mK&(5HRL$AtlE;N0y`|=OV*%XcN{S&!DCItk&!i^56#c2$^4JL&fUPWF!tgJ7_ zc5_<82%q#lb{PM(0`S>HzsBy(&q2`1pPZddy!SLH7hhGaXAJKF_b)316_{x2_`-1h(!$?Q$ZXWBrWTK1TBTk`Tu z>!8txkoKW9T^7|3$CJA`>D{U2_C2ewxQQAd6A8kWFxZWMxP>iwr5oshMr-AGVA}s; zV|=KH0@A_ld+HWdQp)rjZTiw0TO@zQF^^nTEf%n(D>f$VK2DVX@^V7Rav*+sdqFn6 z=i(gVT;8#$Tppg4iKhwsU5r08HxL6EVGJCqv!RM9TSy%_nfA zI zfR0bh`8{4ilW_f!TA96JTeeeM&R7a0)^k5ZTK9js$6_`^|3ypd-CY%5F=Is=ToWEN zPW)DcgjLv9%vj?FDtZMYUKV}C4MYH;PW=P(@#vs4!r=l<`L}NA@+YPz9uULzlUlkn zo^zq|;IzReNC~XVoR!SnF6at+F z??kTKu)FsMqmA{Tm>hQZ@5cwyA~7XH$VpT;`u7Ob|+On_l+dNd6V$o4+?vzKN5;`>?gmp(FfmP(+jSug5@ap zvxeVU+L{?v3h(aXQE<|n9R4&E-?AB~Raj6=fW6FxG)<%dw+^`^Hb&^F*Is;(Pwd}1 zH+C3mJ!G+kWf@$&zh8@g`ousu{>A>Rb(b!bkgm>h;{Dy%$y%k)gn1eI#ee+k8bmjG ztf2DMe@^b@%az&u1C_cSGj+B`SwBVk_F^Q8*t_bHM-jlff&akvpWP9o z&+*=@l1JeN@?^JXZKMp+jeCrRruQ4~20xC=FSK!YKWtN?U}Ivw|J)2ybVdclnZ@pF z>}3rPoabSaXa_u_vPLmA?HB(8ngZH+uLdB`S5NIZm43PDpn_A0$UV4mnC30R#HB&%n ziQRG=D}ub}cy9c)dB^KVPP@T9_LX{el%|uvW!2B6rT99WDhzzi%R2~KKQudDf`plz zOMm(ELuhwKn~kqnyj|rfEg(*IYM8M^{mNhj!nTjmb7MUgP9eq!bSeJ|CEEc4HbV?3AU_-_J}@8W2e;sPiZu^LTM zv*G`wI~v68Pa?@E=OlxS`+*OrLA-G%{9K;T%Kxf$7r=+6t)W`^@yow7g<=#St^U@QaH-VwU4{mUYwtgSJ|T>O#hkE-0&5n zV?41$Va4EiGqOg8{tbSY&uX3>{-_v5L!H2wv5zTT!&s?9p}%uSTJQnfhl*D%n!uB-C}a1H?CRnin?ZYrI} zYu944lvwUmeAk&idI!LLUhtuC8-MjZTu*|olhgR?`NAviu;AdTt-o?Nn2Uy154_3r zBu~nT!ii_`t58sf%(ofEe3yR)P+*~BVqCWjuUCic1bj?BL*>)je7uYIEcK1dz0qQK zS#5B79c&0r>oc)uhOW3++Ni~`-ZQ#r)-jIQ5zMLTpiO+1=iehJkDtP`)4GK`mK2PJ z{v?nmuOOKo7yH|v0zOb(kPs8|nEm*CN?<$vQ0D_YrhpmhO3VZ7YG>F^kf59H#I?Z# z<|5E8R}_52FK&umoDceQi`7HTD^Q=XswPmL+WJq_c|N?aM8AaGaAbq<-lIEvKl|^1 zh#R&ET0!+keTv;#7&ZzSoT_#=9TH3!?b4>*7}M#F){IZ z-ACXP3AwJ!GN&y?J^uBxY8ekX&_Tg~E`pAQ1!@(s4IO4X3FkM@Y7a1UKh z$sU+fhITD~T1<@%ZRAxx`~|xUgqG`LVH{|WgqWFg&dev%{r;`M ztp{ra!n{W;ELf8XA~#WHg*IzM6G)^&GnWD59|L3?R6OFrZceh`rN(Z-F3pEu8qr?)(A+>d@fkF0vm}|kNr$dsPOKwZC!yki_l4y|efhA_l~Z(X1sXlJUaKUMo04O)3`=R7d<*$i3IuePrC>LRan z(;f!+ud6h8N-%K+!u}lemnP?`eB?dYk8n{%rVQP+KX>Yy8NuQ{@s60ZLPLn>(e*_= zS55!b;~a5gI6}<&`=64DFQ+FbWcF1i77tYRh@3<(N9RTyjHvnCmA!3p+h9X;X}L7D zSduJ$I-2cO+F)kROBIckk2c?VKt1fgHR+L+l{H*!_|SeGE0N1MiN|yxxq1!0HsyEp z5$;qU=cN5>WCp$om&CTX`TDy1L{-7yY4a#@Xf>f8d6ENDuq=9`M)w1dSHQ$=tNF;k z((3#9m4Qy(Nt#tt>JYr0^ixAB5Hn zL>-y>Y|dyeopoKSb@b9IKkKITE<4l$C5AQ2v;JgClEQ5-zDSoEf}|3n&3z^_q&sVz z*W84rfdII;uoOwjSpn&+dWjpKt+j8<8T8kks z8TkdVyHjo++28ChP%$2>I%{r_H~qi*y^+D|G$>KA3>u zE(nyqZ-m%li9C=9?OfdRsITOf+&o!M6Ledd&1j7{+MLn&;&BL^0~lZsNQk6fDyumK z^)}P7lKD1^h?OVF8c*N5nEr6Rs11^QVl8kecx8XDL!Hbh-E0VcfY7Zv1Q%y(<wv5D=0Zr8K^Z3Zu6E!I&F*fuwu%E@o-0@vnHGdG5rj%gVWIJ*VW$p*7luv3rpfx=r}y~Y z@#UYLZ|w5@*n&qNqV{k@3f%{Z{bs>D1&t0zuby#%?If?{kiFJfBvj6 zBlEBVKIb^RaI&hc*bJ}Xr!~ouNvYvpA1Yejb%UT(`?+)Uh~X!*Z&0=yc>xHlhIA4G zZeFswHB2RccXUAFU6SGvw^g4Vyc!5$J#t4%JQFhz8*J+aL#6w^nVg)(-~)NEH81^_ z@(Gr1LK2O;n4tDb;d9Oaq8_#wFh^Cm!=W;9=li+{^5&Nw0i0ZPYeIwkh!a zeSHBI>5a=&`R7^@QEzk9%g1x$vLEZ#sMR=~!*j%}UsBM1h{*H>BzSh?4rw3tCJ71+ zqN_LRdVx4cQ{?7KE#-C4OXN2C{}c?9MSH*eACizeGf9u9_OEWiagW^QzAr<6i2?L`%xRDl)But z-d$ywuZl)l_onnvS&l)rMQCBsjXi|&Qe6^Vl2AW?ej?C>N0WJiDLsW*=2+lMdmgQ);uqlP~|xu=_ced^;)8 zJ$DFIc^L3I_B$WCBt|pi)*~xAJ25o`R$kT*+ZK*w-MuQ4>oB!@w$g93+1$`G{3hmq zU<#fWAZDHOPe|~XwWL`69x5)m>pASyL@QwhgkqmM9N#*t!KwtjD_+gPcp`^+Ws8Su;B?u2vH+mq`5W! zDppY9b9^;t^Wm77g6)~AV{|H^XP;NZ)ffw>LFJ#y#}z>VT7oZloj9|q|5YL^QtR6X zaf}Fqu&+iwkl+-+ul>bBcrYVmnaVtnM)yVIV4rS?My5pn7G!T-5s*TVg`qrcQkHPv zHvRwYkbQ{|R8(0>f>{I#{w3#?BSQi6Sx!EcbA_RJ?yfB8Z)_v1e+p?X1k_TyMSE^f z#TnhX?9uxesJfv|vY!>&Nm6TTY23K~XsgZZr|`?Gv>V!eSCC9fN#z~go-{YLvE1bs z(o6!;@$z3bEIaksyB6qcf z%jv<+qx9cG8)M#I_Ui6P9Gj#afGXn2F5s}8cWr4&^86aFB87>{exJ6#F&B^Yx~ zX_of?!(@>R{8$Z6Ct|km{NJk-QX6QDvijtC_|QGAF0=sC9+lwww4ZM<`8={6huieO zpPCfKp&WCYi88IGzTKj5%MX>oO6|Xu^NW&W*_#IcJy`6s4W{RW-%?#TxAP7E{8l!w zZ-iUJ{>m9K&t|S{{J)>sA_$_xuT50!E7n87M*V4Z-OcUNRJ=U&O+Xd1-S2Z+5;4ik zijNJkB0Be_GUwdqJ*$2K5%ImcA*`CUJ{^^dEF10qHs5If(+dz1oRh?%&V@WI@K<3D z&uFE?3fQTPh{U8#UFMJ>HHJ-@uS#e(p_KCXv9BRd4;}{X2-%i|W-wlgGCqtC7-p7@ z{iOy%*2~S0zJ8L^-Hx&q_uSqeg}@s<>o0)%L$n@`ly=A>f$>dzyjt<-`t~+8F@6D) z)ac<&$hed~|<#K8EmV48BySnmpYqJc`yU&1s zl)8+5%X|{#T|E_=1^xXom`x8^Nat@3Wzg)vG#MYQ!Kf80>FC%CsDJZUhCV1%1c}K4i4JwK%%F1#>&S8;yAyirVE_@Vud9*<< ze2F4CXwhiZXrzcWi?OL$Hx^Ykasz6^6FhFws@Hx$UniPjz2M=ZkH-Ui2z1xkscWi* zFNwyo=_0pwy~;w+D_ruWCrcccr-jeRxpKa&2fI&t#RPczt}8cYhs}6I%=F0wZ4_+q zJBkQp%5nPQ6jwCR8=H+nS__=SP(2~L;zc85tCm#z~8QavLl_{%*McN zx;4qB@9VwtwS$_E5?7a3N?8hw`jscLYB!`u5Ga}OA-zM0iS4eU8`(GD*(SWh6caln zt0^MEOj@!DbWhwEX3TW$tO^Yo(yj!cx*tiJh6!@8yimI$+RbrcL6#a+m*NRXL=eY& zDM(T>>|K#1X?!9alg7n(l_8f#}#H!TWA_fsw;ft2{SzjU_Z zn2YWT-yO^d!uQsf0=pkDh4<_WnWM1#>^};Bc16)%ty@GZ8B7})a@$*2K%i~YzEHEl zV`tqqlKR+3Wmv9MgPWT`mXIGC6(NZteR4RA#Utr=qwmKuQr^B%jHqOc9uL8_bE1~4 zJD`@4N3QgaUY4*G)t}~SkZCK>7_yF5@)D$6Q11F!SY4k6^WvkoE?K7ce9KUq3>8!l zqeWAPKl^|wZssfVia^+7GUaNA*3|mcmv#Tv{R3~3Qq7Ma0;{mKrRDs)i=(ESCWO<6 ziL(;Vb$#6bpK<$c4xO_H^=9jh%fg^=Xms0EdG^fi z&;rTPYnh}TiHpD^CI&VDcOL5BblHgD8#n734kk?J^BSKEK@ygU)>yT-ubvIquovYL zAFswCL7nULx8-AKs2vh{heAlRTrS~m&nwPUf0bFjPjPXMP^3UV>3ibd$ii0aacGf_ zFRry(eY29Xbj%8wN3X?7EI-)gQ=%@v>bS~%KdX9#TMgx(#mQ`>*L-t2q1fo~1U~o$c;x|@I;EO+mU8Xw7 zb>$L%Q}TP!f%Ac;=?)_y7GKXO0YKQCF3(SzJ~zP@AuQlC@i9Jpa{tWH5j%A*b_^YR zvj`NN|4B=lv9FGkt_pcp|COj-Ln%b87?pE-^J#)i#@^A}?}bpid5feyFFy@iA1M;F z+c5wl9c&e;JU2=#hM8-LAQ^@XlsjQ8$R{lKCsv*6_Iq-`h;S{NOXgnRcU~V&WFkmQ zNZ|Cn45*x;A|ugIK{|;qh+LZV)HsX2k7S8?pG16*fb%q^GUXCilC2H1@O4trib!7$+&u#C@2}-<`tKEm3X&ZyJ=EYRuimgJICQAh?IlqR6 zzDGtLeCzuL0Srv(Stt6XJK4vnsZX3e&RneTPzxueB-FcgjI!13OuEioe70EYzQ_AK zo{__>t5!YY4MElYr%}E;b0*?=sDY?OdJW^7B}MffBI)bF8H{lc6m!9Ec$hz`CRy)r zVgVlfX?IIPww+@oG+3%FoPyqT8Y09uP}DWvCoa27bMT?VF7f$jksi{7$hgOXXtZ#O zeR;4Kx@2}4z_x9TZdWT%PPhw|n&3lB`^-H~9yT^MKv{vj6P9!2B37X^q47#hP6G?M zAjxFY=7HqRkzx(k#k^rQ+?AOuU2GaxzFXLISF7gw1)3$LkY!$JV;U3^(ja)6@_ANF zm-7Q|6M!ZSRg>TVB)w8JL|hF~B3gcccL*e+_vwB*zcmPwmR+DnpzfMJNA-L76ax!4 zWFBPeQ?;TZPQNVk@(2PS%v61hN!|U?vb)I4z$mf_8+qNTR~>!$f$k|2JA+H6Gv2)J z12?%XG`P8*82x9)-(IlAC5aU81>6vn#yy~ z%L)Mx5zdQz8U=2)4RRhfxPycw<&&<~=(dzx&c{K{{-BUUi&5TI6u}_wD5k zJplLnuJd%?4y53-`vwDr8yIyo<5g*u+um0f_P5tY*^qf(S#Pw@ahfYclkM9`sjZxF zChGf`&O!`>cE#%@XCms(=S)P>b4%TXx}(qt=g9C!fhuMV52a}>UgG`@)5Hg%=dBSM zV7cA(YFzdZ>L)@p=+>Nni56kfsIbcY#}{M(UlN4V)^Zu8&(Zp*O~b)JJ~r_q_tng0 zCja65(W`^O;MKt_dSw2Jb0?>HkF261%;a4$Z=hMZz=&$X_n+a(O@elfLglf9b+RWW z|32}jz4dYZT2vD059Q7E$GgKegTk(BEu>dWg9XMRcKYOR)}2w*@7XG~tdr6c_nP|Q z!mkYax})(~NaV{4hyPDS(Tkz4_Fuwf7oVt<1xlcL9JqNcb@5kgv1wYXOR|~xXEp76 zsjH8qrX4ehAV=SVEE<@8hHLS%m_Oh>0)YkJYI7LHQt7}xIZpxbVpd^~tWlg2q_%V7 z1#5mr?UG1d4JM0z8H1HzP)zrw*B9X@r)5IAtx!9%ShY1V|2->N+=mbEy!IdLfRw3M zbcO^?NjUi7)2OI$2Z5B;RkGb%4EQuiW$M)d2D%4BDVxt3HS%<-OTqhwa-JMcgWiM* zo;QT@-5+8ZjCzee07e)yI|xlnnsKz<}D>>2Bm`F(T=}?@a z&7W_wyayR(;%GqUOt+a5Kw7Hzyy(7#d9;4gLO`vM^eHA4;@CcOJf(qRHhcGP8IK>*4xUMQrW6lr%Q#D<~9YFe-$^k#a#~3tTz4 zz7xHIF4O`}IbhR5L8K4aRH~+|)%4htBHU81d{*=HeJ)$)4{4e-iu7`IYl-ptoE=yW zVSdlu70Fd&W)So{L4L@&d0E+6cc#pBCgMVG_%`MJjW|=;yRe&w#Oo%;NfvQDo9P#q zPCY`pqfD9o{mO9argy`tRMk!QSXK zLRzWY;rJulT6nl9q-YelwU90|#^0nW$-}B{nZPWI+B;PhQ);?CGF|V1Dz?|1oK8#I zv`BLE^9L#5a%E@V5Hm9i{;!MY@QR=3%jo~}fmG}L6jk(8Z){46Zn+$2QciQFb8r6!tMHKb}Ug^1winfzcwnjke~`Lo#0MO+*llSUbgK;?G~k@I|!C1g`- zG_k~=BrrcZJ}Vi>`}opto(~|)Uw1}UgcUAg8g>!ipWVbd#XvL;^E$r9LqkDeEp66z zKDiw6yr5!lqiFxgGD@w21370jq`|w{5zXh)<>8jdOlN#*H(C=xK^A^H8u#OV$Kl3m+3QlXIkxbf|b^l z#O%oxS@LQw2Zbtn!|tV&IHC0JVxm3{4i3cs880j^vxNuHSqk-{&^YwUetQ<&J;AxO zxtgkiu&bTgZaCI+aykGyle54%x31@Up{r9fY&KFagv%aDJRAHDcDSadrsk(nTvqBD z;3{oudYHCxONio+x5;*cH}K#Q29;w^1~~DgPi_gLfFt7FI|IZw&4N9S z5CKCGg06?afyBX4TOD2!t9;2Sdu~o%-o*JTy33}3Cd(u}m?eWrkcX}FEk}hWD{uxr zM1#)!TqD!BOebu#5Kn?aA-_o3(ev95lU(kUA#GbR*#Op8cL1n8Aj7DdvVqWd>|b3b zN_OFRcD2Gh$!|&CZ46#&8WN-~Gi=zOAl^;AX3Ei!WFmrUJ*J)EevlPzM_*IdbLDq! zsW)$vKf6VPhM7L$LT)ks{dj9iJ72wegpR zl)XT2B{R9%hzWluZ`Xqmw7REUs>wOwk-<$BCF4$X_NH%<6qLRcbo++f?kv zY*WG}M9qE#Bhtk=rCJC%w}sW8s*~~IWCppXC{2f*OaRz*JBWEKMpGdb*=;dV;%Gi5 zmwCZ2Xz-`b{W-);p!h3fNY++Z&rtI=*ng3^O+Ng*-raJm&i&1Ph*2bj0U_At985c@ z1ibj@o(R!Bp+KkY! z2BVp|Ih$Uo$uXO!lM(%Urol?vnV_J7@C*kE^AgPP3<3#y*~yaq#5WPd=3mV*(Ec5+ z^w&G*zYoty$~Y=YJ7#_PeL;Qv(G{YxoqBx)G~b@vpNW|)Kg7hGv?lI@;wu#YT1Cz2 zorZ;psf3h|Z5bNUhAbo@rxiRzo>KZbXYauy{Ci&*nR}PbeR($5pTB3-J(@a(E#{XQ z_fEocjY2)ggB9gpGwjmAMXu{duT)eRq)&|XVzQjAj<>$3LfXa?Qx%DiZfS}fB9Xa`N*MtPT=_vr-CklFlXfw{T8?gL7m=L{7r?}$VXPZ-sJzIxL%)aRH|whd zb0KuZr5>f2>RXai;p6dj$u#dQ-*$z~`*d_gIt`^8wcGbV9+f4}QmkEZ^Cg@(ZbE)G z*}@Ec-(SbU5CA)c=q@?A!|6e__UbCiC5j*P;nAWao6EYQcgPQ9mLF5vPW{&W{>pw^ zPuSWee{C2H{`{}{*4gUWtC0CiHkIb91E%1`v>C}L`O6~E`@3{w*R6~2{hL-N`N06m zbI;>7*!xCCkUXZRf6s3>+Xo%q!zk(q_6HdDo@HK*UTYZL2c+c0cNx2r zc&nXO2NsxANmy~vgC;7i9WT$(rMtJkjjk{9;j%MQ)jy4H_c<{=#_p{fsL*8@d&s6+ z15P2CZfzcHD{;kL|?xCWv-Og+r6gV#*MG!x3i19N1SyiIon?1TtCOQ`g z;t1#MOE=9|E3gC%#=XbmGx=wasME}RQMb{h&}r2zJ8eEO?YQA^MDTEJWb^3HeNUgU ztIM-_#&nP#(*ZlG+9jnmrDG-3Re1{2Z7;b~jcJy6>u zWT*KIutx&N8kE41Wcx+*VNeYKy=4iud5Q-sHm-j(3N*Q$R(9?@h+-onN%mpkeDvrM zSX($b9l<-&!D|b+g2Ybb&H`{zVK4yRZRRDJo9Qj_8dp+}%p4Q!Yo`trIE6B~=;>Q< zoH7Kv=7b1+~*{AxIVo9HUjs9mj+nVq)2*{zBuKI>la zI+)Skuc=Q=nb4xIsmTwbd>yHq%*tLR(PC2k^f8lA#5qUwrho@`Bo76y*)a zxS(bCK+<3vJBsYp)1X_>V*C~wJeJi`?`|z?x>;brIr&1Z`xb&wt##HtN`>9gB3at` zh3k{0uPp{bQdoXPWN;T!`*O`EXFjjAp74iDe}k>@z~~Iu`gRHeRq|n2*e&&l;l5iy zQdO^exvW5mE)J(<=jve2)8ls7NW~^4S>R=?%h=h$_f7RZ>xswIICYA`GGG(5zrXq$ z$1V3vO6uRiYgH09E#NBgM6{m%?qn-}J*ZNx3e+yZ=IJ-+h>7_bzN?*L@un)*9(@Xg z2at(ENLa$o&W@r;5m^vpWavSK!jYxfU!_puP^v9W4k|3Qpr`Z%v^1_q!8x*tak-*_{G7Y$kJ*rKEz~ z6Vu~BMhs&@GUhp{YtXcI<-o-aO4od@com72oS0^fVm&bXzK7C7^U-@)1|NB4giP5C za!q+1_kWqW+^VInu$sa}P>xr6ZtS+>{q(~b%ddDnMb|WAyZC!}xi{s`ty>>OtSyX; zUXNuPMM8j)?_xEGw1HYtu9h3C%x3L_awVKxT{B-k$G(Lur0p$X&)_@v#w%2xBN&M^ELOhFfkzk14OOK#Ve9+u%jZ^Am25Yf9}-Mdynb}%rSDtz9YFq2nfoTH#QcNIFG^7# zqrWY?hvdVkevs2xIYU-&Im%`&Sn;RYV|1*{ygmBf#5XNn1Q^d0}l?wZ42G?9G6RdiLU39{`5C$zgl~?wi%sRd5Lu zE2RS&gajafNlIBSTIGoRBz)k#13GuTSG5E5FR+GX)Go4mW~cN+g+(@ll|^f83^#dd z>l+zHM@NqcOs!}z`S~A<<4Gwm@O?^7&VKz;ObpR(==fPm8E|L=CG!u@&p$a^jEXoO z6?9w3zj*OA=w+dVf$Z5*0L0{j<@9!Yu z6+|WX#T5Hv(D>0dfBwPt8cW$!5-;e7ZCwG2S_0(8tUlGJhC~U0LReWe(-TeK zX))zdzQ$7^t=6P+seKTF1uhX5eP3&(z^rm0DAW{EMGF?0$Yv-@_j??AQ851V zB5${c#FwC;Iu0YeVT8SbPv3^?bp;35n7b4d@HIrX7Tyb1F47qTwm7wb*Vm`pHt4Dl%v`B68WPh1cwG@`){6`lYw{G3yEy~ns@H$z|PFrC36t{(o>BMC_ zve7IXSFB%)PfqT!`!}<_#<^^~b<(NQ*YAsLTpL8Z*1>cdzW7{kiHj_4$cH!@jEMoq zU}t6ZA~T%G+nre=2lN~(D=Sc0S8V>F6BdRT?&2382g0>AklcKHd^``94n7WUQRss| z4`57z0jO{Sdu~U$V2r6rKl*;2OBMYPe(XN?`eZdWWW6!S#ea@Z+7fFGqM3PoJCOx@e6ny{JHZWQq=3;uVu$&;I+@oI5_C(rP8q!H;Png zt;_6__%y;Ce5}k&uf_?mdk>+%;1(9X4?|2r!86c>tdAVxYpFxT^cJuenk6K8$9db@ z-v4Rzy@Gp%?*3Y`2keFJl|& zArt6=a<*}Aws?N3DHEODXKHk=umjD-UxqjJJM}i1J9UwGjNx*ooID7C;_QdMPOYvb zD$btO`?_B9{yDzH5d5ol;#|X*jlzl?@=_vc zazd_7(`t9Sun|pGH9K0x53dSytvqCC5q)a)YPo#T?fVi}BoKJFx$6Owvp>hH*?)m@ zAN2 zdMz65PWPaA!fF21)4>#{l;?fM6B@Wr@|ZLjpA292){f9kG&(vuOj2+6)h}WLGMHf*O7BGF2j8NmIU9+oyQsf<^}!@2RGcx>rB;}p?;kKD z#ApQ`2yk+)&!$UK)_jlg6ZM!~NXXX!t?Zt|zFd6hW926=W1MAUahll)s-}EQ7aVGt zGGT`>U1MtLArsuY45$YAnd~$k@8bxZ82Ydie^gATln-H}KIU!KNM*?U0;U=+1$;-P zq18?E+6z0-)>6$@P({L^{nMu@s%=pGN=gPUcYUli+pGa-;^FF)B@ifYh24TGu_uC} z-5!Fz(+$&YameBmKAtW8QU+O-^+-)j(LIskXb5NdbZvaxy5RVN{}akv{c;QEcHB+a z@>CeL{A_=#JUktyrh`SW4!k@EU1sI@e?&6A(zEMviJ{_ z8Lu*{&i932I%>IFaOu-cqVIEPvONt1?K7xU+7v(V8J`XTf&#US1WfjRjrup)AB`+1 zh+k3h_ixei5ywXNZ;KuZQtK@Q&?OfhZ+XRZkI|8k^xI(_mpvP@EwP9$T8GzA2}0EH zf|)N@OSu6$ZpT}jw)M_)sx34M+4IeJ9#DulEPw=uqP_lft?5bTJn-QRom@kFm!E>a z{?Zae2V*tw$Wm1#7}3Ey`9-E22w~lyxX%EHsgQkfeiG5&-w&EcLO)WP&hYN{Y)J$X zHoZn-OzA)h|NQ2@910$)1S4%rRf<7(yx;XM>yPd9+_!re;+V=dH2h`MV3stR#P)G6dK)ZvrqOhIJe_fg2awaoOGA@iu4_pM)6;znbkqYdY2=t8rR!T3-y1=0k7U2+?9 z=MK){c3;EA_t%_Q#n>Wgy_+r}PlF!P#}_FT=}ZYY16&fmiv{YIny)ku1&}6*>XkMr zfGv4I{~i?$wx%a$tCcpD582qZq4z1S+jIE1XRr3=T8>y{A6MPoU@@FD&TVwV(R8h^G17g~jT> z^Yi!;3fab8A6y%?PB-V8LsD>;`)F-pU_{Ge*?5hy>t1vKcNs~TSS#r|d01rRrza=P zO-+c9W;4>Ag*fjh_&ysi_rQC`5vN7+;Wk*cPFDtYSy%9u;+ z>1u@1ktJDoBk_M$Iyw>OSYL-0V%8NYIaW?=#P+V2zqF2}|6R&_x2LzacQS-YsUEOk z{q<+_;JlqKUtdCjOiOu|cKm`)bXf4r*wNf`R>C9p^a$EAc=dt z(O~km@MHToYKx@Ne;}!W!NKM)5>2x(;ji_%Uu``xq8{?W;bH_xY68w{eE?QLATYX; zE?$EL@mtQj?t2w)DFuB-x}u*aM%}@}5pvz4`1_7A{8!16Zs_cS?NAR?g6|iF-b96gqh^r71rC zq!>4^+}0i091=ZLtPdYpUY=P=sCx47qCH8BlKi6Kznyd#%JqLYG)&LasiVf0_+?gR zPrNZ((1g7ObzQ8n0$+x1-2}+>|2Bx*0Pm zeg=d(I!*!(6P8|pym-l6v;e>sfHf)N+$AMt3wl7B`3nY+$>1BKvBC5LLhuFc7l&dZCN5I$j4E@#F+_j6tq@^8t@f$pzY zZMK;QH2oeLSbZ4u0axKozIKwW#y?c~ z!L^axc{@H>3^%;K7?IZZDRZ9Sk&VUrlq;oN<+l4b6#q-9qXU(D!Ti1AI~5aFVB_X? zy){X7$KY3YH=ukS=4IXrIK77QL4biFVV+N3^^4Jcz&%}TmTnXKmOC|%ckeg+3&)e> zb;@q6w=x3h0152%Er$yKZf{qD(gBVLd(?Ipz~o%^CRz9qG?8Jx9<|kA18o~9z6hdL zeP<~_3xkbKDX?(Zp_+B*X^3e1M6+XCclmOxr4nyKCubLmA6r0 zd+za2ctAYwHG5U)*vmH(AK`IPF3xSCt||l@Po(8gZl1^60L_5UpFczBY1F)$H0<+- z0E5P3-eV3_IA{-I`KSMkr?3W!f24TU9pk?JLTJUK4auWDy0tjXO{ATrI?yGK zSSMT7WUba@S=eV9P5WNeLro^?*^^Dg62Cou*bj}#Q^q-Wkp}|fb9PEtS&s<`2=0f| zy;lN!%}DW;EONMiyUw(745E&z&ZiJ|0u&JbXm){uM0VRvzk7!#j)!#$^vQyCnJ|_9 z?Rs91OAYKLuTyu3KB;k96@p2_#3{yrGt9`l8;mH)C_PDUm;txz?06ROo-EdMlZf!k;MZygoo0SXm` zn?t&?#z0BqTsKOW%b#o2mk*?qTsq#fB=oyUfBqwx3vqGBMwnEfbU&4d)4>e44F#2y z9D!KMeV&*kyhK_NS>xq*GBb~9QWDC~?+%l39;Yu>W2J_MKg{@8K~G2I=%7o|qwnN7 z37g_;;whb~nO9*ox`oV_R6$>n6B!37DK`K$F!`-625>C#ZT)-`0h4b`Y-|?L!n{b^ z5#)W;-C$~BmLlR!Y^(;LxX+*cetLIy{jRpV%*gl!jeJauVdqTc_hrs3xN}S5~1z$B@>%Mz4wE>N(+8DKwyr;UN@znD^fP^ zIw~!0YbgjaQL(!tjiA`?HAxtbRU=j?l+_)*+|K*IcS19*2 zJp+R?%-+WM`5B(aznZLynX=J9s$==>vZ36k-#M>OM9>r3ZeBgdYASiOb7=By{KwGT zN7QS$)}5a`k9W`0Tt?5$nGjhkOxJ>HL?7~{<|hVF!0K0FY)<(^XSs&7($V4i`LSBQ zS#P}@$(^Y`x)|rZ7G4u=CDULAgrX2_x|DQ`UIv+is7=9x^`C{XakI3+bkty+gG95qeeeSj$}ufkBF4hsr3DGGA5Pc)65p+bGYd+O}$!dM#LD zwOaVad;fkj5=kH(L!#{SSS?MtrR2XN>s*uoE#EWD{6M z3$&WMoScpk#J89kUtNr1<1y>t$>Rk<j_b_S(i#1oY%vV9p8~Q zL@-tqdyXcFOA%TvwbGJm`}OCI*lJ8vU8f2d-;ST#LSm!S{=-(8)!&|&`slb-Y;dZb z>p$y)SxSRhIL>AHCK&~>Vo<|;q5dSTwpL?f<~WDev33A*6^`uoF}YsIuU{ixAj z?D+;}jUc?T4;NSFr&Z@J!gs#v28pSwF>^i_4cz@a=$Dj?z(Py(dk`J4VfvpWeA3sA zr}7DvA8Qo$?)Iz7o#tzEnvfVe0)dC|H!xT?UE-15GAc{KwT$Xm^>mr!>8DAnoC?eFwZ*~867mfGPgFXCMAWO z*>(qHv^_eKh9CJ)M|OTw$~S5rcxQ$PgzlHTyrfcg$mB&6Z$XIN%7K(6jLYQG`rVB$ z|D}~ToAK9%Sa)%y3)2tl$?I!uq1?2-64kujZOCgH6JilW7U_!@uab}4^H_u{2aG=9 zv80qlL}X+|uOaC-_TSLZ!M%=-@PdCQ{6G+(z)fb&_wBs%mI-nFQrtq>ADP$4aLdVAz6&-k?~hNFbswB0VemPWUJ5v6g}P$eco^IztW5W~#LlfkUsR+r3|mLOFdX2+q! zMD4w=n_W{>#zr5wd*)jhOTRVYYq$HiSTRLl(Zv(ZKRPt@6=dNQrz+Y}R04|##-mep z?y7@MwNvU)3g`)^W`kvWr*v8FmG5T`9*X_jB~uWRcjXE%I%_J+Ml) z3QW?>`R7;CB@=ZQifkZJ(O}hww(VH6vYjugc3S{JD@7=+C$;)o3w}5+1Gr5eK9D9$ z#?~jj{u*+IA8bR~78aLnA`Nz??}Mu^WM!*kyzbKp(Fq9DG$nleVKBKqDwjJDApDBIdAz;G7qxw zFYaXzMAE`JjQ-C_A^HKYgFSL|+HLb+$NZzD@()*w8m`Z;pl<~GH?^<}5FLrJ8(u1H zJpQq5s{k3vSCJ{nct$19V3FqO+?W4)^))nKx%!PT-<*8fEJWL4mH@M^)Z@jvxA%DU z6dN9&nfUr%VJ)&8tPSU{-`8cz40_j?a|1F++e%rZN%{YtzuA9I{5V&ucs$A3a@ymp z5WL?S6 z3?nkH#l=Jjn+xQ~$*VByu7Mul2>wa}o5o{SR<+u}>HkNe4B2rL7Bd9nGcJ{oTBVP5 z+S9fVHxLqGV3^gfyZ}RZ5MJ5_OWi4PN42sWCHK@pcVo9se$ZW6LeP}%9@W|=MzLLN z32whE96=2{of;*vX^juQMH`+G92_h(w%k!rP%yBY$qiGJfOic6mxLW#a<==H=2-kQ zGwBAJna1skb>PtkbEx#wr=vw$6TrlWkrbqXOajnobujyBJ^x)=wVm(hjs6ZFSO)rR zotw{Z&`SA(TH(;6yXRHX(yp5t@=?VECT-STc2a2~M2w)zdCLhl2~@(vU)twuZ2oy+ zA>tJnx$R~<<5i~EF&g$%B&yw_pNk_X+MPWMI)RZ7Pq#A!Cq<=<=Q029d}P1;V)p^I zubVEw0jSe!9v9o!kc#}4K0y8c%77H3cb{|)RZA5K z=xWH!AP3@t)p2-_Ak%lshJ>MK8M$x#kM-_d6s60<@C<`Ue^{Z_q2r_DNEGBWFLK}o7@v*xR+XNp3iI@TCf&2^PzxKQx#f;c5 z84P}P{T_0zms2r~6d&Z;zq#nl0#1B_2a%kv6LpW@G?T*MpX8PX1JmpOl00(_5D_TX zXK0%*2>=FMJPizH!NhxqoR3dPNNvzbF1M9AmCwhby@!&)==tRDD%0^iCiHw{jn~Oq zl&&D4Jqm~|Awyt&G{5&6qe5ydjt7Z1*VwF2pFVxt`3B$;`UmmK+?%JF_nt;m9q#Yn zz=8)S68>Q$x`QB=74rGJz~<=#8_TfJ$XAo!>~mEZyhWtrgE|u*Z=buHC;UdjQVCYn z^c)-|T7zcWkyMGxzwhywXogjNXby>@fN#8Vpp3u2e?CdELhrNa@JNf1?^-4%K^Zv+ z8W6PTP3LhZL#7o4De0dgdBNoFqp3UV0jPmVTsA5O>JNh;&FggAXR^fjDRe_3pZ*y- zbzs`22H^d5Pgk5t{iyZv%QShpTgTs?6hW5KQnO9+h}Hox_TOgVG5KmP)IT5g`Lmdo zI*`?UHvVy>ZA65JTKxHJ=;ie(14e89Btm+U zJb2&LRRF~^B=IT7ezid659jKJ%vYhd2LCXC0VXEn4DN2e2T;n1_I?Tmp)=567ooKL z&ve%0%Znp6dI*w47wZZ6B%#&px1u5w+pnc*5q{F&jfTVF-A~l>S{>{aVF*H?=gaF< zZng71ZE!#NZ7Pljj{rkSyRk6H+{{Zf`EV6e1>6BmWugUumumNFfI!dBP|p;$-{+i3 z5q|yqD~PT28XT&fYe#gcS8Ggm?@G=!u|g=fCY4wi$~RjzUP>dmEQsBXE(@Sj~{ed>c#@G`q@jfdg= zq<~<|qYXu~L}sF!2&nEoH{Xte94>2JpxxeaxyKubD~p zh9KMuHO@j~CGMQN|1>z7wBf4BCw2L@gp~NoRQ|D`ZM|{j1Wq73rQZ((f}IAs;rJF< z?zpb~LBWzeKyQqp6D5}Ghw~DJhWW|eovW)TbSP|~+KR^Yi^*W56m*J=in=_Sur-Yi z=6HxDVP?^Dr_=Wt_>V`ve{5eD$F!%b@i;tX3DlEq_$XvJ8B~~D`B>|FmCl%hW8;T2 zNX&_q`~w-IfH@3}jMS+(m;e)3*M*affQQy!t~v#!k)H|FKR z$Q$P8SqEMAI)1)QuAPk#Zlbr_yoav;Cv(FL_4X=nVs#uC4(Zq56gueK*~Tl8G* z<(KD=j1(a~e-ANHv}@5nH;fQ$w--tCTHRwv?pCi>0nSR7q1+FM`Se#$P9r2I8p@LF zRNqVj{~$*wAj9S4e)k(cedx>Xemb;@Z-@^)!^gwbcF<;>&DC!6+Wrx8>zf6YaP&RV z#|#Y8rMvn)R}N#Yoe0d+h*j`BXqP8kG=^p@|7i$~AI(wd??|0LRC9 zC+l{yy4SH}GsbQB)xyhV`~km2KQCnCw2AgOq%(S)U@bDM<$Qw(+(Gl6@q!FP(&HXF z;N@j*3cAQVeX3EQrmS(*=)Lv__u5X(PDRK5vy>Gs->F>5Y}_(bljP zw*T7_-=PZGBiu+%-oAP&+tJ6m&;w7Otv*D2mU}B_6TQ4@o+{$>4-YZ4B|9$PL!6G;^Co%4#^J$OBMk1H1&k&uT3 z_$$yUbs8Mp%g1Fg(^22hGEP!ERVBQaWn|J^;s6?VrF+?uqggMp6fPg0^zN^i`|VNG zgIo?v8`W=V4T=j@@W`3Ld~7(;)q2kLK+*GDWd+#>qRALY3?tN|kYNyE$RrXMgI@EzNK z-ne{P&$~sr?*`5F#CO|$2yhY}Ue4Xw0f(H{3wBVg1RMT46BKaW;<7mpg502b!Bgwx zZitdCX?@|A3M@fTBq1$czPutiO$ZI!E9C-rDST##fOcxn^TA$G-J^?AjJIofrI(wP zGZ;5-iVm#eBLUEWNh|pV{Btip?i$);d2aXKgdNcB3`$`SwYvLONV7Ds)N|lbdhpINyYHGt4 zWhSPkke2bKJqi`kZXceZCYyJca3L*mwM3KE^>~~;62!My#E*Fgtt++X=jSPLp8}l8 zkd)-{r{3ScEv5_H-|Q_4^M@O*_=s1)`uNd_$F20QpHp4*n$@gGt=h4hC2LfZFUy-9NDmylE5}v9OjIpENi+S-lS@ z|8$OFb5XR5vf9O!*6IHo`s`F~FrL~S$wAip)St3w6jB^6<$TwEeUI06V=~vV;|a&p zJ1M^uQGx)sUzXiq9n^M5Oyi(;j95?aq<;7}*HV9Ww3ZabZxs$QE&!K@ zlT+Na0)!Da=M~L0L(MWKNgorB^j%7db&2N4MU_qWc)1yMw>YGf>7|i;xDpLi<|E5# z5#`{Y0#9>j-XHmL#O8H%S#Q%EO ze~Q3-dbQ~3w7vT?g6(=Qd16*NF~4=`<|=kFwsMR16Z#|l8n2cdeA$Fpkq>+vJQY%q zP<*s0I77OJ0rv9GgrWn5ssI8x-kkB>TL^<-HQVXZ)qlJ5o$nv#E7z_S+Z>De`wg2( zW1^#>bzPdc*ggAJv`UymgQ*^opn^rpnCaJbz_&6jAy$%ni5n8Qd;)yEW}0{vNnXFe z$Sxddb@n#&6b|oO8@SICzA|4YR@7KqZtKsIPxM`K;_*@rFVq|-lV!cmRx|y*Gm3XJ=kAA4^E;pcsjA1Q+{DZb>^woa*O?6q zON&Y5LzEl{F_ggOt(}erT3ejekwfL^c5@l32`e7Q0zzsQf;I!fC^RJ)s?E50?|2%w z`P5pGNvymfNuB4nIz_z>ANJi{JeQ7m-_q8GOL;02_J=f0@kHB4>R21)Ga6&lrU`01 zYG%9=7OO%i+#KOrYzSIM$qye@(tkZb45!PlSC|rX`aD@D0G>*=dP?wZ@&Nm%M0r!5 zE(C!%UZw`y*&YG7P0e{oOD_2vXrY0W6D}!y_8IVy%WLykjc3et24ehwJe_q^Ra>~l z4+u&LCy4U&pnoe`j%9~fjWkAqg_Pt!I%sGdo8~hnc|uI8;qo_0BV#od00?T5!sdH3xepr< zV7#H-#v#Y|r2_Z+;>#N6JePt=qsX5+^jz3+OaxMw$2;;cPzT5KXysAc$PE@~`~G6o zaa8?njJ!NMI~xG*>mwCeo~mgCBas*Yj*)`~a!gD{>r47Tgg;n#9|T24Myh1Kw0MXh zY4lAUwC(8bd^4FVApMM< zt-~xYa+qn#U%i4Y?vfWL9@YL~cEv>i`O~WO=Nfi)g%V*KLghm_T3UqP6+ng8E^M5d zN+m(#=Z96EIly4OuV`&v_HzJgKp$7XZRYsAE1Tfs2Z(iE*#Cf9Yf;fRE7x*@K1>PO zWcW%O1XjE4w<4HT(w>gyl^(U#H@rQ-qf|&9DK$+W`zTTwY-hkfb2O1JLDsfI#@_jM zWET?{G`g@&Eu4X^%L7892ZyjA=3QBMdE15x6665e(j5>&na`6RuWqP-2V--%D7|?T zAd=uesE}{CpkrcMO{njEh$ikgSLx4T-2C+t<&rmv74(mZQzx+5K9^TW7Bm595fB@B z90Mw**^;Z*C+j=_=U<&kPpdImVS81}z63Wj;de%EnfN*WhkvRy*4zwwk{S%Xq0~vp zWz%}OHAI71x{e2_xFHB{_*JNIfc+89;!e8;QWFlyk(8eNCufwgH6oio1N0Z%F4H5# zbn4s{5&|Dn2i?w7{+8RuN-T@m?o~(N+!ztq*k$B7K{d#sj&U*^^M}=YrzTm($?~Bb ze^}Mk;XDKx^w_lO8*YD_sCG^QI2+>1SCL$3rTC{k85Ip5-_X*Y3c35ydfA(^awrpo zPU>)F5Fe+?6~48)!ze+l}ylc8QOZvEyn1W1XsbZ0$*3J*qJB58`V?wnd;Rc zqwjsWuoRp3;&5f+_5RqF@W|P3ho#=`RI#hb{0K-jXJuqqRP%bax4%9Wy@9CC?+j^M zt83oT>*rSJf8KjPwU<7JLP`aT?O`C$mHF3kB{ACUZNuG}!TG;do+sqILFg(9i|kjw z06K+)ry*Qvq}81J;Z%gn@Og(PaoDuBaXnSpcNGaqi-xf5=CaG$K%P9|S8?c#4VLH< zgkflFT*%s$E$y`5V~!=g=y^fu%@3`slwP8gy?gMW&X0=bwm$yUw_{JyJH>c0 zC5G-x0=7Pnp;tcJ--nm)$Tas%Q|Gq-2jns0Y2?75I=5|$zpOE!q~czz<4nvorb$^zk?F8}w@LYR2{q75Uc5>}2X~8T zabmdN^bs=rLuzy?W3&!)JTp|D4mP6xqL?*c8B=Eoo^k&IqV@5zJ*Zt$%qp>}kYU%W zTz^|d6WU_`(&)+~@RJd~KE8Crg)@9rKpJ+uY;sHOCC0~BhD#23X)Qp8IAHL92!61- zol(*>hEZ!?wa7nCySt(6aUslQ;9%EKgG*tkN^NPtO0&ZH@8kSeU-I#jx_LJ*>>;}> z;&I6$nQa!HIFO?W^AS%Q8ymP!Z@(+Go_xFcg&m0BxuijvE6_Es#PGS$`C@c$saHNq z{fBV|*yUuTp0;1pc{ej|FzoEuZ`JM%LSn~da~vH3)J_oP!`s{KB4kcHGJFu?EizjH z`?hn!V%?od2dKZP2lWd^&J;2PM5na_IbqX%E?ZzfQ@@P(e|?txQrn;SiH!4PiLrD~ z*H3R-yEMEmsHIp!eNghMRc!==bpPwXDcG-JuQN``o z-sOA%JXZWn2MJ*WD0b80M=Av_wn)CAN^(|L7em9L#U|drYH4M7si2TDGf(=0c4eT;iDtX zhf(Y>xiPiqy)Rsj^(@VBd7T=V%3%%W*h4r^=C4A{2qWZqFGhVtqL_($;C#3~X^gAvj3^9*9^v4+O#@2Q3WI)ik}ek( zPbds(k-=&uGqJ{vPRMJo4FmLT>lPF4LuSP+hvPyj5N`(DDKmVIh4g$eB#3hLMB;>M zD7S;(l3;{lfCLnFmU#oi=ZJUbN{noAbMkpZ+Z`_BvTcd_KbH|oY@7uk6t7K;ELWJxCu?P;@6;g~mK!q;=cV|6b+R z{u70Bn7$jPL3UT=5w`>R%`7kC%pJcXbs=mxTK48O5xE0DoH^c+Yzu|^2gghpb4&v! zz#?c!O#PJWidgT;ODK-{R{2ArTR}g+{D-N!G{$ct^z|Xoukgoz%TH$Zlf6LZBrhBT z7RmzT$?m70S22HXAn$CQ{%Ewm3(F@8UdNqa=&{<`y#*TD(AG@AbuST~C!4?6#s$04 z#w>6;ZC;@)o?r94wi|g-h95RI^pFK>pS*@Y*{g|LjM1=YII@?o9BBDqB_dmbl=J-E_ijNsVh@Z*C)5nI2Cq8n>4bKE&<@o;u z`>+PDTL_c^_1O+YmnPk{zE)T)mccTDdOcv7-s(XOMEEWedt7tEZ_bIL3kwTZ?S(jI z+Ql(sTz$l!tTFihRhZ+9_c5fF;BCXt_%+*)7re7&1P>JcTOS8g)e?rGt0?=e>@#`B z@%nghNiDcda)cT4pdBwoCp^J03hU-$B6*dv#F#P9zLdh0`z)UFkumO#|8NFIRwsF~ zCwK|8n_K;tEuKeL{j*bL){R^81`3Qk-SS5ZdEGoehSQGV)406%o3l>8fQ6ZaTJ63a zKN)WlP3MtAMZyxlL}Rt0lfo5iFH_*B{I#kow~7Pw{^uVVXf<dRjhQD>dbo$?6Sh*bW9(9H^?nAKVy;8(*BEG+!PoA-jxc90PNaaO1?xfVuuue6ro zYeo)XV%PO}Ex9Py3+Y`ME>Z9E9UG_B!6?())aosQPcVvfA>_>#;e8}hLM4HZ^CXK< zm_n(+yH`V2&VA#6FQ|4$y6&{Bj^e(_%XKET%8Eq?ESD{c!${+cQ17+riQ7hDTNid8 zn}dv4UR1l!Wp=w&RpDlpJUMeF7b?#}Ak*s4CeOfQy?S(BQk*-kS1@fOPgJLOd%s6* zzwW5@SKP;~{w2&*ExmF}2%1=}4de)Yz1kB^ZKBh-&PSb;1a-5yjlb2@j0IMCH`__A z<;?wVvpDsUXG*<=x_Or?p0nqDAuZTT8r&papcc?2c+pA4>5!z9zQN- z%%Gx@2(oYMzkGG|CJOmr<^FxmoNlo{`N#WB*bx@z?Ok19+2{Dv%OS3z9vHgSP_>SU zsu?Xrqqmpn??yoN6oWJDmWF>`J5mR+_-9o^OfGFW*Ns4rYqMzL=YM3AojH1-mXqAQ zYi(&Gl~_1Jt5*|8zB#0%FpEbi3Y7~#*z^SD)65C)qEozF&H&}hS6%A0EXjSGV;@=h z$T%R#PTV+cFf7v5b8|a8q>KmlHOSz-`(7RA_Y)5ZQax|6_HRQMAF z$V`P)`;F5w(4iw$6Juf)z=L^b1RYyhdH#H%OPz^7r5S^G6O)bqmwo0?tU!5fBXwY5 zaQ>J2#{fivz3h0dg^9+1b}hR!%~NyX1cFlWVUB{J=kaiDmBDh5B91?2?7a-fm1CnP@5f!(=|0ZA(n;Yk zGL|$phA8N(R5P2lq#)WSeD&wE@>%@_I(u|dU5c3opH6Op4`T;zF6p|1!|!-v!NFK6 zgz3S_&o!g_vy^V#dLJ0{PFWJWCWOfQtJ^2yomQ}%UY;hwDK?treom)cD^@4I3!kp_lj}Z6y+)gg3Y&?JJBH}w z6dtu}r52Q47YBT0Rs#TPS>9tL3jJ>34tcO{vl3leslN;v@9`*a-@d)Tf4MzF4ZcTs ztHs(Z#VnL!-okWr(y=iF^h!bR-)ZD(`{CauB7(F(e$KBn#-N87WUXu!NzKEXg%*iM zm)5rf(9qCIZKsGn*e#qN6g24QR=v$2dXx0vvEAJ-{O;2DXgLFKPmim>+8qsM3Ecr? zCKeS2YAXYK?>qdrf=~&>kOA=}FbY0<{#@AcxU_QtsheS`?fPd$Z3%eN;Nw&G{Gl3% zFNvc^ubHDUZw9MfNr|Y2&5!5c>1GEdC@S=zbKY`%k9bqO#$Bf z43s{=!+ItmKlGKW|?tKy@n5bo~2TrA8+-t^UXw}-KH zq7o4Az*Pt|-SCJAzBplxZa&)`;a>a7V9o0U&c!v%aPiY%!34i|QwwB~d8{T~=Ym*a zD*O5A52;Q%o4DSYQk#+Dxx9+0Yj~Me!R@a0_S4nQ6TW8p6G#E5 zlur5-ITtjz$d;aeh^LnfesvE5Pe+ibf{I(poyAXT^ol;GBTLmrwSTVeyQK7*Rgeif zTDK%ogXc$|{J3~%DM`#{CYVpg`~2^IW23l2Kf0fo*C|==9vp~}NX$n*$J<4CVqXB) zv+=g7xVV@@IuO4r9rUSG0grO88t3?C@gJFMAkX6|G7EX1e}7dp{&DnlK=9XIQ|XEt z5I+{jGBBu!WSdaqZ}Q6CMG=p^rzTpin}3#kmQBA65raeKtoRFA^RSqOK7>$1gj}6Rs>Lg?$U$ z1{^r)g^G`4A1&^Am|E%}(xpw z@%>3T@rjraKKP8dfv-#${z+&bKSzU!qjsUDZc^V1lPa^jcReqkl!Ob!PzE#o1NS=r zhvtc96)%7v;xyOV4QZ$x)v~nf9OZ^V`Fc%QSeGy2iY-})&uXw`;cf5n^5;*}hT@;T ze2I#Q8L6~S|Mo=x$W<6%b|2XFM(3QfG9~~Rx;gxO8%n{zNGh2HvK3P}}{U__-C zI@5G_2R+DX?GS^CpOtlE%{Deg*bNJXM&n1{#f}X`&(cq*#~f_%=H9iuPAIwD}@@6#Au9z%twP&w@); z4J4bs2YeRX#{+&*QhnTBBgI?LG4>-HCJ>JB88K-vrN+jd{B5Hg%2N{p*Kn`c)vTi+ zrQ3ITFmF5Q!JUu|R%0HU@mG_z>~1MOM|GS)9+!Lme7Md-sew4fLGVj$cUuEakJLA~Bj{mj^2!sp7YP>oq=@nIa_AH*)sd#xBMMBc^ ze4Abdgpho#t<9%EQ=srL|I@5jrwN09KvY~vHDnwyK#K#4@}g2mbZ`ECtMu7zke=?&G>2N7Bm2;Zf{<$|s2H z6xscuBH`!fuvp8C5gXMPwKJD$`?uDuKhs5+>rx=d$SZcIFRE{Ncv!_G$bfnBs{!`d zX@8L}gd1!%dsZM%BFOQtVh4DC<5jgKOFi#`0{{K^?72ICU-DU*Wl@aRKc~M;u`e0N z>a1)H4Gk?V^_ri*R8{rf&U!)=3bJJr^{t5a?*h+G#`F}2MxHD|9~~1btBB*Vot?6> zoE%p3W01feN~r`Zax2X7YpOu|X9O6+F#SzC2kX+LVEYD$`5@X`RMT#vNBarDq=7Dx zL1c{Gd^={!QjSJw)z5@Bj{9zPeLwYXb#^i%wIbx7WwUiYXg!{->Vyqb8UuxZyLx|g zcDDJG>_6NW(?8RpZ*gYE2*`h}yuae==G!Apw7^nDa5pVI{cuZ+@a|nQA_}Bc3qEPj z7Xcr*Rg$`OvlPjbx-FM`yDZvU&}?#f3_c(&AzlnCJ2AUH+D*Wm3R?8d3zk_;xol00 zRcs~ldIhDXQnRv#-vz~c=j8ogujPqYb;rx(<0>mHL(=s5eT?SSsEtk17rW2Q ze}&kBt+|!sxr$0;T6T0}%~qYys`}>i&xrXY;;!T~$P*klicnO%Tp9Q0guc|&xibP2 z7Fkst3sAsJ-e}tIMz4et`9MaFG-WAEAt}=1)Ul!7%Te!YeUBYHg8Lmg?8Rg+bak09 zVuv#8J?*Y$wCL+Jbv1d-y3BLTy$-9EfVwmKO>HfzuMg}Ddi7tX3XKB3e@`^9K?NnA}A)NypAs5m-)<<8=NMS<>j+Q^!`_D8Y-mwsyFpw2Wl2P4wqHxq{}ko z8@!F0S`xSnmb5pa?4s=>d2f6Ba$i}0>us^;NxU$B{px+u)mL0RI~(-?vq;2!{AA>H zs@VSYjc(mZ`~B@5+RoU8d2X9gmiKqH%g@*LmiAClQISYnz=&KPp(FGSKc)(M07BN= z>kK3#qm&mSD zy+8dkfYUxQ1xsM7>DT;x^zYECx1eC+lAGi*yJe*96=8)+)5PP!Sfltj zefJTu#;b$-YIPJk_$R#$+K*rNyp;1Zinp1lCKGahPJi0PH-*3oR0a3mmKcOY#^Uza z1(Nf7k_AOh<~fp@L3z7`OVF$=OOdGHo1O^@7q$?LEl~G6Z5X&PUpk2roCB z;H4BVIx0^6NQI2J518Zi%Q(v77%2X5R?uN-QcITxg9x`}wAbwXZ`+0!39+%6mX^(@ z2c&$RsN{)!wudLg21GiZ*osdwK1qBYMj>k z(nS!*Cnt3mgF;ZIuqw#N%$%cEq3EpG+_m3D$!l9KWbJVXfGgXnuN*$-u}@9OP!iLGqV*-?PUKv2X3R0QB?yd1YptcG&8 z{-z5dkg)lW&yv~UrOUGK`O}uFU1Q@Fa5I6dQ}ruR623wD&5TlKIXUg8!5W3ML&!4V zpPJ=4?Cyntr+2c(g*~TXn(cEJjx8PCRR#$H2OS+9ry1^5KU|hh)OYRK&mhr=l}|I2 zXHkR!mNq#1_Gw^XyhYpqF`R{s4Hp+TmIm_TcKp&JB3~@04>~k!l(n^&9jgI_%%B`= zD)Hpbok;(OFME1rNVpRg7Z%kLY%&diVZ-wqa3|jB*Z;twr7LO>) z=@7+5#|kF*#4nqNFf?M|^4tS`8;I&Rzl#=xi)u|dS2kM6U+sF00PuX8`Rql8C_3vE1(GWdhUhO%Y*^IT5IaE4&O-p#>Y95 zpphbnq0iJoS_KqmE3@p2UfV`+Mo;4Dl1pvtvf-ntR4%X4((m8V5QYMa@4LmSU-sPZ&!VHFB|Bf) zR#OtW029}3&uxl_U9VFC1@`JUHJ;dby37&tJE^S(v7v>xfJlF6SfVpnFttB<<`9JA zmaA2;DJ4bQD|!Zpv4Y+&0?oucM#jdf8Yo6B$GR*{8S((mxW>qeKNJ(2+BmG;?VwTU z=Z|G&WW>bSjx)WF(AnTcD(l^n#0XH%kf%q@>6X4HhpCKKFDx9bsz)E@3+00%E}ONt zDBbzl^4&X$x?!HQ8cJU6MT-(^%1VSC$tc-a6cbrV%%)$e%>pR2kQXu-v;%SgaoB1? z&tZ6sBaNXFgA8fnz-ca4&u9&M#hqmdm?ndbQO4G?-(^v*eqyOE^$KH}z|~6Ndc9dO z<(>59%lJ31v+dRn@vJ^)`TUXxwd&l-nGiloSm0qq)^`PRS1gwS%YviSFIgQs;;C zaK;0Bp@sJL;Q07>5W_7Jr7>g`@;djza{iItz5BAx{iU~eJyO53-!Qkl_>q;qv@>?B<{DM3P_J3hhZ1}~^S$s3QPy^lb;CuyVk;XjhSi(fd>0?+@A zk&>erExL`#D%)RuF}4jCHm=&0`C5XWYt@@fNAAK=_IWIwBqpK1{LL4hgIU6?kVV#4 zyq`x(B~SBj7Izy7nuM~(K>NhXScr-oXTa|6uJh(_&FdpSks7@9(V~LfTpX@t_cwwN zlk9xVzAY+aN6p*GEGzD3w4FRr1N!mdJ7T)c;bM<&mo;EiQapHI{M7z_PGnBgnzL-Z zGXPy~%ejZ$QTe&>7TiVLbiB?T|Fqh|y`Y*477)ONFtwxzY@sfeiKvuDqO)6^FM+{+m9 z_I1SO%E8+4*@@Fe-$?Hazsn)nLwqz*28K`2{$VWssX@i=78Q-G>JVEpbMMxu6d@}h z#-6Z{P~Fn#6oMrV?(Pcfv43XvFu`JDW8>xHBSvuecPIJaU}AY&Z>OvLy)!VE1Fsr9 zV8(^|(>8{XEC-nw=i;}T{i0V9o47Y3JddukxVzV~VvI4v&+pRP&lxa9dHbs)b8}LA zeWd>ubf_4Am;sJ2qcaZ0FKt=uyzm-BhbOu%uB@z_yssM}e6E~v4;_Wd@gT7bE_c}T z&lFNPdN{EC3v|X5wXUuaOwM; zCA--CcwaB|C^=~}41ZQz5&CZWp`e>2_XiV|V6VM2;XQTE#!0(388}}p6Ellhnv1nH zc=YH=On!ER-{(4Ah|}-t=@E6Corlc;<*3WQnL;aMrj=kb34Km={+{qw|N8iLyEL6z zI#tT+J#4H=pQQkYDq_TqwzrHcWjApuUksJiV!O4@O?{FiL^0MOqM~7ZY+N+c`hx+h z*B--&!@oYhBl>AEO9{)Ge7+6-MHCZxR9fWN4P5(}_A z>Zr^!q8C6APlb??mZISOBi$;AI8(hLDHZO%K5YMKXOH%Q4Wh~rV!s7BI%3`vK z&ET>d_V?ttw-A*RDYBpSE&e`gTcW`7Wk82Yke`p_AJfK+=&4Il1rFzGHcs2i!s12A zwYc&tRLpDh1T&36RCxK@9KE8=RQ-f;(MtvOwi~|eD@)ybUyX|Dt}Z<&NnCvESu*+3osP6QL7}cCfpL+Iyc|4)DcO%- zP1Ro%vsfiMFUWms+>@6006PnSei`A8E3y~t03DMzNpHwW{Gsey7!b+Ij?6{i^<(~o z)PKvKr@bs^d``-T-v_7Y6=QW~-MT7-%wOP{2&}HMwG@;akg1k<59eH;i|!C0E?Hv! zO^oy2{&@}?6FAc*I_velG6K}dlsKAhJ4E4C^O5vo975qV zva@cNL1A#h(zy5UnjoQBSCVOgMAFdHtH}-74l9?K2~#;<1cj5#!2uMGO8=bRIvjb{ zp@IYZi{cXW8Nfs~K(*TD;%3m#vM5XPVJGvEp(z~q<)61Gt12`)iSTZ_&rkns*3r3* znQ>i;``i-)F_>py%k8+TZZk0cr?va`FFDx1!!F+RjfosF>*e~NnPwo#u<$cfPRorD zr~3Tf@w9%Ps}hp6+H6_P_FL8UsX27?RhO*+HgXQ5IA5=d6L>=@9-FNsoH!+>ow2wZ zW131~6Q|8R0Es<-s*f3E4XPIHA$(L>Rm;KW&z?QkF84eEUpDN3+qQ${TJUME&zH=o z2wIR`Brty0*nLw`^it@@mjcAWf z=~f)8S%|Qy)T6%F)?)wuU4|C9Q<=|5#nBq_9c+^6rk#uNMhM?!8bLuM(t|O0|zgssizi3WkjA3ElhN#w_psAE*Udvr_^6ni^H@A~F&Yy6a8Eoq8Kesw8 z)%U84zN5H%H_dh5K&Nepqvgta8*%draWy+@9M^e|a8^cLQRQD}++KLsy`lS$IHZFo zzXtAU@K#dE&8(iFAtok?d=8#FYP{1tsR~591#-*y+zuN0$qoOg*fMj#e{v+|oZAE%EwtI8h@Z#d@+RGaT)#k^~je3I(z?wB+PQ+MP|JwcaA=oct7qVD5TWjE+) z@D#Wj*_p$a)KnpKos5^lx#3^b4@K6(ZTH)Bf;c}CNCM9&t=&KT7tY@4n(g{&2xgJIqi$tR9_O8LqEN?u1m(I%3 z@eYB@!5XQW$SxpCao=C8(vgfpwx}g$0u{AZs~`mm zQ^0q54)wOu;ojBybzzX0`SnyLD=X_4eapc{|5^^M{6`8QX+{BnCQe*zg?tR<_nr-G zwmDK@yryFT&&P#Xi`C%rAo}-bpy`h1GCR1pKg z5e_xN5X0k5>aHNXiH^QQ*88IB)=k_mq9(0{Gt>a$gVBPWjN)e^)(DUjdaX3C6 zo@i0uLwNJ;oezsm4Pc!q@roNn6k2S=*4EZa;EA~Atbd|;{P+pJC%O7uM2JIcvF(1P zTQVPAql_Y%0@4^@#saMcm0R^FBnEpoB@p%N1g*R-UBj>a1N_Zu1$0WWeGOrB55~@4 zzx=#wn7}vkwXt!#&~QqPxXGTIP`<0_`cfl%`h#C&Jg9yGG^E9~~R2 zh%VYFDg8NhOSenFR`CmN-de@*i;DxZqqR#KK!W(|blHEov*ePrmoq3)bD|kjmnM$# z_H81IUcG0}xEuU$H>73nE&6DKcJ<@5TDDmCVq$T-aQk%R{y_pbFS z9e%-~?7gZuhOe*Gyd^Ddo=y!f3kwTJ7an4lop58KgilC_l={1halQ{79f^rlpZoYo z9ZcD!2Or$za<1t%o}rONM=10^X_hqjvVC5CKnfds5a4vn&F@fBE**5RHwdfXqe=Y* z-T;&3U>6_o4YgyFc}<_QYwd*ES*dtnykFwy`F^PEhG{}+}-_D$hNzDYy+7q z!f$5?0XA4sIiz7o^1H(_+qLxO?bYiOH8d{h^gUI%&Vx(+eI)+?vA;2vWi`Z>>+7UP zj5hOEhEmjSdb+A)3DQ5nOmWcA0I&8?DIE>h!{6q`?nKQTO@-Khw(*Lg=34gaULhez^IrSo2`E>z#kewnjTbEf^U2Pn#G*fG~t{ak|w;@rCrmV>3UFF`frG? z4(6m;NWo@~;3p&D59*FC$LFJ?mxrJIwxWB&#d)%Y30CPBZ<=o4;dq*zm&~<_jFq~5 zzP;wnALC)UIzPnkr}0HUu(EW&6g`!NjMwvNQaLL&b_jH+hsLtLn4jm4rHxSqC0)C8 z`!kU9r2gVdVx;@s*z$nqqmr0NxmQ+sjq~~l+=ke_Vr#^P4nn5C(|IkTCQ4Vx3lvh` zW)yxh-LAzGwf@JD>^l;fb}o5Irbm5*Q|SSf6zS3tD-%mS)bBrDZ?a~Z zDI$V#oCNAvtYqc$gj{-RjhbZuwETPFt=H+wQbGcFY0K4WA7uP9E2-o0pljw}=_DR-PUU$IJD1>JQo$&#x;iYKer}D6 zH#)vy+co54F15l&#hI2QCIv%d*(%L2&NY7k6$E6xMmYKfQ7QT^+tV_Oy;tUN;|ceI z1vpI~T9hN&3YRRaH{Vu4gEr&39$Q=*gD-@|Vd?(!=M$W!SPQb32TEiP0<|?XY`sKL z5z2(WfJgY{9kL!B0l&R=fO;e4ZI!IzVdL4>+4&wjcp_T%UcSfO_#9;^r@wNOvzI}# z0@i5-G3)xHNtA(EW8OFCoDUy96cUpEj2U3Bt{$Da1|M=?`I|MN8W%i_?auaRXS+`5iPWaDDW12OhTy+~#>_i45W>wV9}ON?P=7 zWDtJanYIY)?9-v&1Haq&XZPkg2{)?q*94_7bjvGYLnee1rzxw7iIOw0YR8WwMjNQC zuKvB?qX#hN8ydt6L4vNMct7#iR$NIr93jwcSB8guo7B$M0Mabn38x`v<<@^oO*=E| zgQib|eEWQTeXo55>5!j;0NIaK>$#nFkg`}`@bQ>QDP-!~F7V&FdGmDj5h)R3P6-Iy z|72wqlsNr4X(BB7Ejx;Hv*nuU4xp{-76x}&@%!5kUg47y>fFY~{TPg;K{LU@xI`he zH_@?mPikfS#|`xlio9^P7BbRzb#B@ks}0C^)2x@MvuRQPkR5}W)E8{~=MK$n?{_9Q zB%f&z5tg~UgK2-THUOiHDVPDXzpNepq1N{8 z2?bYIWvjCQ4Nd%#TiTt#$jntvP8G(owe_(E+p$>kD4N^kr`l(S8#D#Rm5CfUpOYur z7o2U!3wHp^Dj=XBEh;c>Su*h$le7B6i*^nsOib*au6E#eE=MF)L`TPp9#3Dunxjle zZj$@MUET5N=T-$=*e}9N7>+f(R)dc7!Sru#N<;B>3z#$^Sxa z!YGPw=Sl}VgL&U~S#9m?d;*33o-ZOv36T`3_yrw_3?V!O1R#(0qzDatmAIJx^YC?5 z$@J<;APlPD=RT*T=yP%U-ED7p`E|`59=>uzBTZ2J zP?W+wi)|)Gi@)PbLY%F+xv3{rZ0q2e%XnmSv9+05X6M3aiAyTYZ##5Zm_Iy#=Z zd?qW+oHElIvyt1RFt(lIn%)Geq>`wPc7@``sZN4^1sU1hFUb>?4;u`RChOwZ^=!s6 z53*IL!xaLmjuXy{oyn*cU1~y%#Jg;9w@^?UShYxKgWFb%$ouj&+M5uxF$bmhng+AG zm-f-60t{xB6!MpX7(3&+G^F~ujVqNOY(1#)oJtTCb2W+ZZnl^Ep}L?e^;ca=>z+(PiJ1brVl<6Z&FKBW zPl}EjgX$VuF>4fiK|#}*L1DsZiJ4Z3u`vUGrZ%@~#^aX;a(ZphzH8KV49RJ*!#Bfx z%iu50lC*3TF4-|-o{gnsxNCvTpnBbE;{&(Au!t_G3Z&>Q>8-OnKmOo%G%;4Kz^pO& zNeAz5%RHWWpO3wTcvl|R<-^&G26{(gY9{%2&9nQn%)w6#kK;I4Sch0dQVVaV$adB? z7B=XwQR}*CP}x+B*;JIU>}nQas>(@4(xWjkftDHUtiLm;?oH5>e`b;`scCZFkeq zcfH*rXFtzRKw#>$8ATuzDFj4Z+vmu~DkyoJD{EfP^5^d|GHJ=sXlpCvv3!(g)JDU# zN0dJP#bmbKy|nfqI|K_s*2`&~p}8LP;yyXA@lVz?JMbr96SLMIrFf{WWgr^slf^_X zTec+vDBP#Nc_0T-IL6&LrTt^j$<<@*9?@YPuR7e%Lp%<-v<$?LH5I@fyl3e8Rd<1$ zMwaMJ(`gx;&-+P87^w$!R!$48w$ z4C6n3KI2?-5ewho>`r?{DGkXBpSd#RDwr%t=Cazt6y%UAcKqOx(5=%gySzC25%rOC zu)qgP?nmU4mDR%swNW^I147gsq6Mi!1}cP1Y9`jUuANZ)aU=7GJIonqek%!cvIx-X z;0FTteyG5p0a_H0->3H9_#p#+XlDE3=RZmUtF#7l2+WW^8;6-DsCVDU&T;n)U)}3X zr95s7fH{Yw9?<*E!E=}d+qk-zDRF}<9))!M(>QNWQ1ZmGA1gZ$diU~PZT0fLtV}SOpdMw zX2se}`Z%&Ua3I9ERW#Q&VdEm{={eUrqM4)36W)5&sVc`fQROi78{k$ta=_sD#}K+C zpfPZnrmrH$N;Eka2~w>6;ku5Tg(Y-A9IT%7Z71wS|Guh3_g!vpa;;!tO&%x4B+Rza zMO@Z7oEeO6G#KyjaGuyU8Fo$92Of%zqi!=xB}@q#oL9M=EtMYkCh*-3ugsLtOn282 zIKUY)`jvAfwt4B*>KLf3*e@->`Tzy7{LrJU!3LJDp7cVNv1&bs#%>e@_orIq{VB6u z0~Dh_etG>5PF^%_pe`bf+vg4VE3Psoc(a}o5OA!4OlX^{9hBNt(EE6w@1WzL;;A%s7b{UN}DS9dJVzQw@OF9FUa*?=s zUJS+{mw|%vN^HE#c{NH<3EHjqE4=~R^ zFJtt5j_nKo`r-E#ACA1K@f&aAIQ1lV%y?-I0{FTb`nqTE^lUzU{qILo1EdQZU(|>^ zF|~9@`R`3#8DK(SgyZ_P{2G2^O#ijc+bl0#m6BI)dAs-t-gf-?id`4wKw&)Uf3Jdh z{^3OY&bP}OS)BF*A3CwWSlqh)4-yqxF=cvXVq1D-JN{MnTP_MlTSc7*c1 z;owyXcH38JmQi;vTnOKBU%%c-)r8pI>!;cY|NZ6P>`yShML&iX0Gy0y{9UU_|0_%D z2M5b9pT7Ll;^e7`dk`IOz>)G3! zK>VP>dcqQ@_KEI6f{>##-`Vf5RCq{dVopEexa?R9D zNjz^xp(6R`BXeZ^+h2*{NxTb7#0YT!m4dyj^vzhJL~f?VmBoF82~s)mQ<;R-guG+W z&WQALCC2No#vpfZq4TKs9DP7)#OLE1azuP8rsbi|z*Ph9U_(6{S7-5;dpA&i=Uu$% z{9;ROv;NCTuffRRl2|17R}(d^<9&50bm*|o(L*3cauRy}&|Q(%_yql;h34&O?RdW?CIs% ziU>q79H^RXq>&JCsWx4I-da`{SB7N;KII5;2}1Oi8_J(1Dkk}j!=!HEVPvBgf4{8S z{O%K*k)qmB{M^D~Wq&D)en_ry8B1IVq< zpFf`+ZN|w5=GYrvg&{Ai zKqDV$FG&?%*Z|w$V4g1IFjaOhEzWeqI6EIFW?t=7ib!I-UeIEB$!U7}JwCezYB7O>nW+) zV(s3~uRrmY6=x4CDM@$ZGp$I6-jt5PF(~QGhv1k`=0f-BO#3&maOCGf%zv*pyg)}t ze@|7;;*9MLgiN^1x+>MGs=(97-oQ=8E2?{dAE@kj5Zn^jQZ(0E@S>s~mW(nhdXKu| z6~dzmR*UP8fbI$v#aA`Nt<$`@8HM9f&76YBh?_%!850daiKt#xhc4#}emSOD(UZ^wf$=64K~YTnY+NY1Jl*|i_NhJ(aYuVAp_gVv>!@0E34==yz;$mzS<-#|2a-wa=W=#FYyEe zTm}ja@1`6w;)qeh3V)z*pHv)Q8B*X<2&%tS?JH?Z=CNuMk{52R&|@1gF@Dq*PFmt~ zq`|XaEg8pVu)4HOzzXABK5J=o2}9RwU@eP9b-tEH`5j5h zcxD^??|e4;L&Z|^LCvV_%TEhxKdGWU3_+c>sr>^*v9VG}Ip~$uMK_fAoXzwDB%OTi zA~HzgP7K%Y3BTuTFP6qA8bvdNb%`eCZU{%UlolucJsMdvl2#_HK>xJtit%JS;TOuv z9Ltds;EG$HNIwdWRHADgLi0U8?{rw~PQLnk4o!}1dc`#0xjICWn1F%i<}01_Xx%rc zhRU@unRt3bU{zqnF+`vyzQsPGl6pq7$7^Qc{xPTYcq&SW^*-46ra(Con#}8F7}li7&qs6A!ZT9*FN4j@0<#b|aT){wE z3kDv>;=6F*`4VkihDj0O&xv%5D8i(usCC(zNlSaALP#)6kY?OWTM+_L*Q#x6iFYMU zO=IH}M)SDN8{5NjGkyt&n2`JnG!}`FT{->^89b zgFijF6|qP>Kcx7B`~iikuCDh^pH!$J9RGN#-50xbD%$i!@~cx*-(1xPNd1zc`bhFW!dIrUGIV;97e$i zr8o4(JGL(^!@N=X-NUl*0u~ zOs#-M^jtQ1l1g%l*8DBtmV=PfMoY0Zz^gz7pY%GKfN;{5mNy>4zfrH&bq2hD+K>bq z+qfABmd+qiUPt&{C!jw}P%!j(dmOHDW%YjHtSQ{?uu{va6zP$oWZ(3$oqF;D*J*2{ zPDL|)K|YQ^QahGh^zCuP3-9yQy@LAl)lPWaV3=4}jKRXr3)ZLhb_|Wbx4q7Ll08zq zE=OR>E*sUdcTNx?_BT(y;xKuXFMG;xB+8U8X8=8=;cbyCBo^Qx@S!cW@a#qMFN#t%4y5uA+xW5fDug66BYSTmWF_Os9tokymaU93OIC!k z9V>ffWRFl}@14Cz=6n0S?|c4Db)4rp-{&6J^|@RwCW7uo5p!^G&nt<^!7iRu)uL}ezN~AG(571j08cR!>k(>5ryV@mD8bLvD*Z&fTxA*nlMg zaM>ui4a%(h=;DAUR=+rvNU`&y8OK>;IV&q`s5_svRxr{!;(IY|du}vDRd1i<+kAPm zz-N0cfU}#h`TNOO9=w@1*hHbYQQyWl5<>cJ_zXJ8ZUxdPTPVsH3luLwRLGRi4X19hmuTQc+wqu$%3|Tua;m1zVh~=R=Vz;z8F*)JN=Gd8`?YL`fdF*Xhru55 zpsqGMPv^;KD%Z2jwVJ=I-2c|s-h>bjcK^&tGBAu2{J-7+>iXkP9uhs~?h}Jsg#jeQ zXFKh;CERRSV;|_sm^X(K0o~5au_-J~;ZLq_c?$5trdhduqnZya@&}4J^8tUv*|Z8iZ?HdWA??(DOqf9>Gx$1Un9N5k6m2KQNDNYydB)Fd)?-Ux>KxUq|l0OmREnOeoCD%M#J5b50;_(XUId!^ka zw(A1k1==bs%dS5#IJ;F!S{x^iri((~g`p!xGgt6`yS&(DlE>M<{Zc8cO$V*zji3MJ zulA5a)5>}fu}JpaFycrQ@k^PsN|m(P^*`N0M<D# zy(E3i^>9Eiz8eoWhxlH=J;`=RSbR3VbSqj3jbimFa|55<2`n}S$&(=uspj!6V`B;D zT8)Y3tQD2w&NE1G#fLB6{;U#OK-Qxkc?-My2OHxe4KuqH8-`*8*yssA>>U)grs@P} zvXJCn6PCke7a}-K9HCC`4YjWb3{4hxb=f{qe-c6szpOU0%w(bM6IBHr;pQyX#cT+j z8|DZ{=p;q!=G%l{I~MJa%b6#_nUxjg7{bXiby#c67PB6vj+MN~lkl1vt+PI=#hy55 z{DOmDNdA)6I%kDKpZc)nTS8S%=#$D?wX_&i^i+dsgZosH4_s1d_mse6%y7q`($~-E z->$=S2e_}FK7AU^cE9ZqGzW6YyD@0zr`DF1)kcMeqH>5N?BSvOQ~_k>`}g!wzHT z*YkR!-8Ui7d0=;MuhwOAqD=pKkt!cd*REb>1_)m|5GI=C_{BdouGin`d1q0rTz;K+ zLk9NoW2c*SeLNlZ8Ijd-DI`pT^1dTy&A?g$11-opJTieMxgFX(OCN~9?#vYIK>1l$ zO30kfl|nj7#*lgsAQ2xm+yhn*e2v(2DCvaxi5UV3Tg94r7Z)iqSu$VK6M8-+eM5~; z&Sr)k8yAabW#NkarcR&^b1+LVb%?Ic}_F z0RE$EkEJ3zV&+9v*ax5K=#Y1pROTkK<_yTvFU^rEG=&-xuD~+Va1sPlrkYMF-tkH+$cV0|FJ!j_svB+9t5@f zjNU^e67==PJ#8c+RwLhM?Z3z$RILJq1+@JX6pNrgiP0J@h`cRw@u|4)CFf&3qmgx+lkPSyfe4wWGCBkdRVp)dPOM0qckI6w}y<8PUR3oO=QI zuU{r94Y^Kwkf*0gqrEdv080A8!qYUZfy?%VR35~$M0<)>z+*`tO#}k0v2MGm%$-C&Y3Aes#c$8U9&%aj^zOCdvX15(-t;cXsG`G^RMCz3Xzhu@O&h3RO$;H6|c zzOY;4x%=egW#KM9u-L1emwL~)(@9u0K7C)vD?-^zk;&B*UYEmV09}rKDN|t9!%sBY zQr83#gtBM}T|>r;CbbG@rO||1L}wUhj7*Gh`6{7x-#>B94PgxA4THfr%BC`s{yr{L z3elgV-@k`M^&hc$y2P|D4hqFBC7f~(1_Xac$uw-8fneD-`wuZ8p&%p5A+|V>i;Fvn zcSj*=xYT2IF7np+3Jovi$anL#lErRsi_PWbcHW<0sSKc3VMCK+SHOfg92zFI**HD~ zNGd@Z=W-R<6n{)h29+fGV+<3J`paVx8vO2^*i68%9+~fla`qc zvd(LN>>TGs>pN6x-P>?H*R2$F>k6qGh}VaOgQaXjEwJQ14pX9*mT)wGQ>0t_CePu< zWVXPnlT6r*lzmBbC7-W7jGx5*4rck8ImfTRvAX?|Ayh5?pY2-$*1~I}6duz0I zE6H*?S=y1fnBJhpQ=b~V*G3*59#cWzcl$%Tnxt?^^nTOQdZ5Gr-kF=9Uj26e|L17kH1q-N=wVB zytFxebr~?#2pV~K3WHZ^5adNe<*LK(Q-0Q?yOxf{j!-mceHZ89$7N^^phM&kgOu$C zXOqa?`c9i@?QHvvfzyW#AQ^(w59DvbUg6Ic!E`75eBl=$^VE|0S-2}a%|C$XWvaLK z{cyfbX``7;krLVrfZuuBz-V#e*;xrG?Xc^uk=tt6y5OlldD3t9@qXAy;R{0~QpA4j zczv&LN3y~Gz@TcivbkiU2mg6`D)M6>FX6mbO%llyLX&uYv&Kota?syjNi*^Bw0!!s z>T_ZDzjz!OagRg8Qon~IjZ}B-H)ca{UW}AEPS%uTY}PGz&T3d$S@9=`xt=V9-1a%! zaK8(u-El`VNuJ4MJbjL>2B{nh6%rDpa9c zMjJj<3*8zjAOEQ@0AA*Qe>y(k?cf1N8M0>qT?*5{qLXKw-6pZL@3Y>cxs&Jt9CCj+ z`&8^q+~x0=wXvlhnaG1o-~7UNvN8Ywl93}{OAH5q zEE+?gPSg}6Fza#|Y>ZT=dlhl#y7E^zj23zf{@EYfsym;0tI32-LQP9anU$krqih4Z ztR#UcHMP(pHs?Lzr0&Ij!NK{RiS4hsD1%vWJ#!u0y357o>cwH-u?((Q&&+MuSK(qX z1TO#6cj!f#!mUTN*4^Eeo}O;%=M$`627>R!!@^E*kvUKMU9Rw1=oT7?ZGQzH#06n< zOl+(p?J(dlZM3t%6YU|z-$up2#>3{*{IFRrt!u2R*aAR(X9pstM=Rs;%dgajvo(_405AH(o+b-jx1+J9X%8W}X#%w!8Qs_1wtL$IWkfgmTsmeLN*Kc^2`J6h zNF=Tzq?66Rp{5wA5Eh)Ow)Z_++Hd>9DB+|>#vVdahh3#OIrCwb10U?WG_&;!G+E?9 zz!Ayw{wih2&={2Vnp8QmH-@?3;{p?;mew^>Cf~+M4E!*B)>w^P!|cMWzgI!o0cZhx z%RQ#mwvbedBbH(ZxbKfy5kp0z3(L#HAg53tLX#zLT~75%A)R|EDyVL)*ESzLUSU^m zHJG^d*w62*#WBwEqwSq?yK!z#$&od|N|ig8DiZ#v9M1L#uO%B$fWaf&b!*6SMG#<5ps99wT5Esnesrc1E96+f9+_z}hK2yt{TVy%xk ztjXjsP0Ua&Cn&l!)aoFOA^n)B8A4~<6FJQ)$NrMGJ2YptfRmUQ;ljhVS}qXr1{J~< z`otAC)$e4mcB^4LQ#(FeQ3{U`ft5CZ$D;=J_xSd38UdTG?v&6}E>@j?32t<}{@Ai; zdI-!C)dAN@cZy7UaZ%Y}#d6!<+?wR1rvI|n`+3{0mR?19yJ{-zih3`N&edGEtDfem zBPeswqca#`;q{{z-G6S#=PN<5PczlWB&GhA1yeDe>s{Xn5$jW>*&r06VnPo3ly;dG zf3V_Pq6)s~4MTHm*kPoQeIqlMti+B$e;$y0cV>dGe@)rg4TIlr|EgzdxPfgEXYRXp z&A-af?sU}8KLSJ2{u3z}0p;Fmg_1sqR+EfX3U_|0|68i&!~JPdbi}4pF6wbzyX%JZ z%R$dM$4+EV57j(st&FU2m0O#r^?RTDZHxRI{12T6H|Feo+&Tl_qi`bTQ7DyYlLKAF zIxf>9d+7&)2DNToD+1Z=5w|P7`ZmME303|c-Wq@G)4;(cv=M+q_ltZtOp(z&Xu<1+ z*-FlL{(*9Xe2!S@ft5M&_>30ud2Wvfg znM*%&(=AZsHs5PI2lC*;_(1#W(zDh&tHBqWMPk0E#_K)j-xt+U_W72|>gsu=wymh# zf$WQ_#dbLD=mR0F8fQvoJC{s@Whnf}(^t&+&feX?rg53xi0SYo#nUCU>Q|&AhCVzF zGSRSwGTBm#8pr*{=Uy><4?#*WT~8px8RSnY+d+6NbhVb2Y2|q^D7e}89!<|#@Dgt~ zsy!2YGCsTxy|J(7N_P3fU&@-vKG37r{IT2ueZVfs*-qk{m-CyZIBnSDala2zzkb1e z8TXoc#}1Cz-Rz#?7*tK~xp^Im9dRaKLDsPri$>4J#>Od_fDS@WQA_6#@T8(&X8px& zL|;fvv!i^&g!|T>_^&q_qLX2}#^t?X3?FDa`$V(7+#pZm(X@WxVI-F|+WD6C|y zLy~o>o|Vz7GH#(S_n3g{@C9b)&tn^S8_d=%=sz|^mng;b0G zL)Mb$vJ54V$d`SUxmMq~Xthz~Fto?TP>(m}LBQ|}&#_iYoBIR$y-|ugAaA3{w=lhP1Tswq9oDj_q`J9{{_p`-ntP4qYMqt7bTNcBzQ#J+#LTF zJ_hxL-_&mK&0=Q9{mNxnp&J%~6-*`XxW}>S6^KfIL3{b>;*q}Q_mUg_*W@rifWe)_ zeQR`YyO|^y6SVi6H$S5)pHQ`(1tldkHvDPquuxA=MExU%dbos;K?gZ#XkQhf?7#df zP*zjNLc01N7jiH>cdK%2v_KaP@l=1WM-#Daw`s%aMjI7iQhR2V8*o95O&<7u@D3<@ z70#JN%&FQE8vR24w3Or*KTR^Vx>|}nLU@b5k%d ztJ-Syv{c`&SAY6GQ$^y<;1=p;G_lE%Ya32@F(()mbGPbFiQC?i?*94`a=O1VnXd8J z{ZqW9zVR@cUfy8Wo}^sItOFL|7vy0^e{dsj+HKPn37t&5@Z#1s8-7bJTv7yDk!rD? zM+*kRAKOG@aUK^viuw^nll~6TyxIQ1Dk;aC8rS!dt3rDUgIPsW-%4yGvOV!wpEM^c z?`eT%UFJ8~WL;{q`#hv1YJY2|2k+4;;eL`@tNWea&TX*o2&&t(XTnLKhz$SM+=tIe zO8r#7n&X+~y%}l(1X`mk#kMB}%v&2BY46gv=Isf${Vn)?JJ_5#>TrhX2;F}{M=Y`# zl1TR-z};HDF`{w+r@{*~%1KoUq&&>8U|EgE9B+Hkq^>qIopoAi5R{hI4-`Szk0~lB ztlY;5&DZ_0vj17e)U0%rk+DfI0?24=_q{!@rzHPv%tRoV|0v(Tr?ZSU1M%~aETW_o zmS&d2%$6zHr-={XA1x#o2S@+WP|+}wj}VL@?@>1FotNMo{a87)(nNR&~>kaRh!ar9R&)`iMb%tKW~cy5M0JJKc5u4{QV_; zH!py>^L6ZajpZrSouwr-zTeHhj&InGe7hIlo6Lq4X+eNr$a#5^1ipsg;6SRezl)|m zpemORcMMwsxLP;g#7W-a2Xm@8OP@8OZhqQ!{MY_`!he_(v-A}l`+Coz-eYNZ2FREF zpPYcdkFHM3XeFDBr4nwWbf+hLIm?M^A!D>-`QVxWaV+9XtlpLgG2nCr^0}fA(r79 zIAl2E%BLOl4kl@2{FG9s>cI5Ntc-@A*0F3{Rra*AAN-dX7#KBfD_YqPxuTRPIgK`R z2MjCiL3FjYt1wL~<;e+(b<=5S^VzS_k1IxwiSk5DVO{OsKo`HeD|5aNSvaBKN=#j-*`nW=-%FT99!+^^AO}LP9-uPuIgNY3$MV~%JBqp` zbJ5s%dfR(jy3(**FL3K|5zz2_6%?=#f(}!_6^I(y5LO>35JqG3kizHr26y*ozWE-& zvoT^qJIjUpjrVlKlojdh3>uOUL`4KV4Fv%-x7*jcONgn8KOP zKNe=>cUrnB00IfUICf5s&tiK8UfCGOhxOOgOO%Fh1xV- zF#`A^Ct|dTg^wvddRyzNo0^*y_q58(*|wp zBHqOHF@r^7e8YG9Ylzo=)!qfprdzBRb#Sh2u}I;rdN;C%pnf>(^!>ZeX~YH2EALgE zVOJ#5GJdG=g+%RSg?pb#uzVWZ^YQw%PBJDbdyRsHmV=wZ(o$CW!}C9=1gLxhz7T^YNRBBYUtl>U)pQebkY?MfCq9kA0-3M*ua-T1J&OD zoU9eFn$5z0Cka^lT(ri!u4dd6cc4t%0-uB@@q|Hd7) zv-MIQF2*R*U5^PcJNnJ{=>NmQqCOb7dM>X!_f<=6j(hd8g;J7H&n_>+=Mxo5pmKHw zaEZOW(u2Q~ED*)2jfZ%fR`sN{QlfIJHz!rsz~J!b@$&92H*g>b);m8|N6AR9%S{Dg z6RqWjil23o$=HwDlfA;8KYRU}r{Qe1yi>b~ocxc&`&aP&25cbbcP&WIYMwpEA%<+F zcjkP}yr6NUvPkNop{9OET5_U)xHWxwFg*PY{Z3$s!#kmtAB>|;$8t!j>nXHMxl4(P%T+_<4^8{b-Rp!YZr4^>u!H{ z(Px>|_PvP>3d|TSIFktoQfYVo>|AFBYMP6>fLl&tmU`0RiH6L%pxgR@K$Zr8lRO;( z1N?Alm(hK;sdZ7cp6f1;$RGK%2-8}(^ZN5s!-{8`EQN$H&twoea`7~Sk4yfW_l+L?rLN(6{0HnP%He7VOV!h4! zT0bkr;4IQ4WErmapqp4E20kSLgqXHA8NPTu$YB=#Dy4D^E8QY%sI3L_f{D@}^-qu} zwqTG09^*@k#+Za9Y4v-(Uhb2FM?aJ{G5m3GCS_&{aq7?AEHAxINGbBu1~(2RrIy$q zK1IyDYY&AsxyIa03MfOPg6eg(m&VGjb7+mT+oxa*hd0wOX!`^A-{~n+ARmE(!s439 zbC20->(V4)On+EB-3`i-=IfLJAn7{sF%IAovdLT}vunm$yhZ`fd76Es_I z^lbr31^|~)kp7+ z%mon>1DA!hC)1Vx+PN?`*Hp>DjDH3ljE%!X{?FdewV!X*iDa8t1rJ&k>laWwwBCqW z2q2lpK)AbYxm})<_?(R1(1@hP7k``f_uKfrkoWK3!ylJA4zz(+X|qoJRTnejFHmF7 zNMxL1Jr(jEeKvIio|~pdgrLBOt8zW}JukYfrKRN?9GS$A0}Ee6=(1wyF%uQzxeY0A zK>*+%ZQ&4>YWp5A8edmOj?X*e5Z@p!)fDnnWzs3ewFenb%&SMQ#-2NS35ZU|4rpm)!8|NHk2 z505Fub8bG0a&c)1UG6j1)|j{1(dJ}qDR&TD7-G-Avx}yF0-h5e0c)MY?|PEtP;b&; zLWTFuhgf(7^%B2vEBW)Vdd4 z9ZwTgxyZF8#n0oA$I9E5o12UHf?6GVF%Fx;YuBzJWTP{HXZ=WJKtQWN_XD5@_d%GF zGGAx10+<2{3JTCmOL?t&KYIYk62j&MHRM{beuRE@QkMH*mo=_Z*Dd2sYoX&!YZeE+4uXS1R1|T31 z_#7pt0W=wSOjF5sA6D0AMRXpwOF%;tG^6LN&fMBdF8hTJ8ghmv^B~Wc~?u@L*0sNLJ5HJos=3l{) z%!GL#!_QAi(clF5f$Lt??i(@UHV3N+bdBoQe%eB+gwdQpg51`v$p5u+`*{&y)=W(3 z1`(v|$~)6+(J_Po;-^}$9eb4xNNK}&v6FsUc*)2FXhu0yY(Zw0m~ypmby zy!@$d-kf)I$t?ZH2PeUQc^FbFU}{uTq;Ie+agM*eNQLT(wf8} zWA~vtDmi6=>#$!49U6Fx2opTqbQ*~7v$jn{8zk9G*6TA!@J>h zQQAl#9QH@gjW2JuP^p?^YU&P6h;4x-aIG=mO_JZ4<7}D&a5%-FF7ijGM_rey)s#mV znEX8?sdyX~!PUWoz_hfaXxE^QZj&Rh4q}^O2vcou|SgB0ex9%bfOP&N^(%4>yLrR-zY?$;ohP-Xd-Yi{}QC z!431zA5!4>IJZOnn$VJv2cN-ruRgl??#cYoTb$kA-)BCm-XH+^3NFKK-+jKmJ?(5Y zVYjn^?7qJvU*(-_d%h3ETlao;J?=2id)A#FuW4X_kDv_u_H?BL!ha)g%WQrO3WJTO z?}(A+toO-^CJ5VQtT(}l5I$2`@xox*Yr0Od9Ym{LbJ5Y@HwH40eWlm_fQ9Gb`VJgS zOTEcA$Y}w6P`K8z;ii$oU+yqH1@fFYzw?OhjfZcGW)D1u3l06?q}<(gGG+zSz*LpZ zz@N?{=wR|tVgX4BXYrT_CWkk}ca5vHexWQ0z$qvh(+`BycR2YmS_r_CC6AROgI>~t zY@AS)ohmYTICmj{KlY<2;BJ$M!eY?r1y^%FsK)bL`VJ;4gh>++5#17h`cg%OOJCQ| zjz%b)537rpu&E>dz0!az^3RtUSyL05D}{}-SaQ9QNX(;fzP1O$u5vETAN%9C+u!~g zbe)`C4k1mGrN+K42iKmX>#je7WxsQ_IgabnbFwBP)#uRi(W4oQH@DUM9%w&B4jeT4 z)63y@^DD9?v4wKS58N0HCTqjBz@C1;JiE*<^9cVnJufHlM~RciT>ef3=WO>3Pd`kO zX3AEc$JYOJHDUp?KjMjAu$C;#fdN-DobM4WDQV-Lk+L!#>Dzbj?&0QOL$7-;ARz4--x9ZO zBlyXytE**UXDdNV%#=aF&_eM1(aT@O^Q_GJ0I>*s-^EEVF^JxV$*f#JgYgB$p+T)< zsldi{_bQGkucwN&Ieq5+3`{p(Nt*ciT_y@$r%<4HF;T%K0sZXdg!8=E2Th&921$*vCPI@ z@QL-M_6hV;??`U#c&Ms`Ha9idOJ7si8=KGmFnMPd4#^AjxUUt+eMdhjg#e-ByNtE{>|*rZa_{tH zgN$a5K6N9M*+KgL_F_aJmK2(nW($>bd7_pQ+d7X%s))+6O>EnfWC&c}+Jj%Q`XsL0pVSLW3S*e|0p; z!@UPY7e%x3GoDQ0+&*0YJs(Lqil7f=M2u_@o0vUSuGHfsBO^PS_H%&hH7qm?QVlQK zZVFicOaRi=g0`==ihg8IckRo$7QD!17voC(0vLDFx*PAm{;>4-=O<@)1Jz8d%h^65 zlHBaP^k&_VWFz4&NNZOlk@_gkoV>Cgf)6B|oHo}-vDoe_PL;y*4Ou`-Ee9hL2sTBK zQc;mo-kRh7IFxVvxX5J52jf&5eP_^aeX`Ph@`Omr(`{IXtJvxZsP_dUCP?SKw5fDnVwES#kivf{S@ zS2+dsG-~+mf%xKxE#F|?aDs$&%0iqzarNhv_&aJBhqYS@cS$hq9~f_GA3nZA)jDjc zmv>^F85AD`4~rd(JL}V~Z!7sUi$B1~m*U~_pkVs`vbgu&HOrjsJmGe;=P?B@M$;C7 z0pUC5GeY)sG5@%KofgY5lmy$`KVR9kJ;WwlPbU4PXFn@`tZ-bbu~J=Kk=PVu3Oj+ zUo<%ynv9I0o7>@>LTVgk+-b;&j(*bRtdi^MAK+bOaPSf`Dfff91Bu)ts7Mxb^gUQD zhw-DO9_9iileQ3*{Gsg%buMuTV5Enkbo*$_(iOcT2cFt3Y%~N`NV9pd9>6`>`NGpc zwxXjM`(=i{@g?A2!6}4B00%G&OD2S{Yni>@Pn(--{32`CXkU&LO$LX*hicql^xT}k zKjH@?RY1XKEXeWM9DHo*wb!E$;JQc-scA zTfofrlV9e{M0F{r7neKrmAftg1IOf*Yk!hMZ=(2%Hk>_~7|VabXI6?jAU>}(;y(Hz zACmp>)%k||lw-9N44TB9mpI!kqug*g zs!~XW`&pu*3uCtms9mnoF*4F7h-5*P1~n4Ic?nQ4q?%eq-{`f{5bEmrqWGY+6#RCY(f#tVg&x#ApDvsRSffeRJ z(pz$`-_R1bJNGrbhZ2a*zU#(81^b)jb^T%wEyYf zUQqUYaJ}P0^L(33Qp$%VPIrKi+3)9kVEFpuK9*eW@9U{n-7DI@^xGbiiCNT&=uz!S zDRJ=3*>CyvLAdrs-)3DM@T^2@zYgbO;^1IoVb_C1H$2=$t^+F7U_$Ek<*j8`4scFL zx~(5~d`KS<=nusY+auTo9QcrVCD|>uOz6CjdkX*+ny=<$6tV8T(H@zaAmkjZl}VdK zhIpTt$nVthv2@z=G>KvwV6r#Jjl>*x7tW6+lM)RK4|Aw2rhN({!>>R;*qsH^+l}YT zy{Uc+AHJ5aF{g_8#9iC%SLM-CA@gVM1PvW=h{f@?=Di;QK=vv!EiyUUH2!X{us_eD z>{dG|?WGL6DLI?jp4k{erFDq}Y_WL-6n{Ev59@kNM*)ovo-s?Sg?l zL4-VD{9nvbRq{xG=;zn%NA@pK|5SljUsi@YaEPN7OyiDUlL`7@H7 zpop9?{1$N@a~`B#@LA<0rW1AJv^ysJplu&}UNM*UB(Qjp<4HJp-|}_y=(iF6c?(DT z2N8>+MZ?Y8Z;QW8b(=)Bzu}vgpk~0teg9Qbz=UbvZdLPY16zxQV9E@iwD+@`po$un z$7>}d#>IW`Rs$FR;Nt|xqd-zE3NG%#F^D_?X_c;y4p5_OA&D@~@~N`shl>s-5{Y$? zWC{?bdma=Ti+iysBV9A(!zj5{hjQ&QIv-m~@$otSO!y5sRk8aZYKPH8a8Qs9Dw32= zVks8`M;xS$YGrzKw@>uco@W#pP)>A7O4(10j3iJE=AC~2jVzM(*k(d{^;c^vMYZQ~ zh|7JkJNafVpH%tGcVU|>OrZmmx!14H?yzxo!w5)H!2Y?1M?&G+pD++plG4%Hv#IJ7 zO=W91sN`_wzK+EJA(>(XO|j#^62Yq*kTI^$BcMGfh@i(*4EIh`PZEZHkIdtWv-U15 z>`-?IU4FhT5&-hk5SjP<;6+4fX>+r@B;6-VeJF8nr1-(TpoP3U9Q}bQHT+$qE_1|< zkpb)8jI5zn^!_?eq=skUd*<9aujx-IDHT4akq~A9Kuq+RTPBPoz*JT6(?tc9 zJt#4bt;Nu*rRb2F;SVP0q-vt5Pk)x#3~bfp9liUQEpzf0-1TEE?d^7<($MLPMuJ|i z)=?MkSa^Dd2WSc>y%_pg3A>dVm(2`i=332WFq+lxEF~q}yYTe|*L%ov5YUa*II34| zFm#g!H@`hrKOwAik_W~nuI$GLii(~vqP6Z#?Aepkd)6`W>h|9n*mQ{)GpwxGK$p(V z&!47SkOBQa)%cUJH>b}wn;@-T!`o3#Pmk)6ftJyCkDYAFdmSAQYCZn$vZivX!2S9* z>M{9wFckupEG$GagM#OjE0^vYlMR>IpdoEzX+zPy=8KGgn5Mj;D+gIatplV$fbTP% zr~VkFC>OEVT@N*CA`0J)yVPW9tb9$k;CVtyN{!Rv>cqrEObof)U6^?+il$ui-05er zem8DaDrEEde013~-*ivDIp6c9@Fs-x9r>fV!=8BlP?VO~G~T6v-_#AJ1*X0SgV8+o zk?$f*N}hgdpBG}rRF3nZp;aIFZc9r9v;J^mbN*S0~qy{Q|2QA~_I2j!9SP?RB)1)pK(V z|9CfsAy6>(spVigCUb5HE7-XifgNZ2XD61Kg{jV7@l8#vx(CNy+VT9MEG`bOwA?G^ z6SuUYQ(l|Y+K}=++zESqSy|j2PZnb)Ad?m33$AN=D@qcB2>+!`uJ|UlD2Z)PeGNtN^NXLzrG`hXK25C@o+KaqGQtd%&dG++P@~E zBC<-Yt-^anjkUe=1p7N32n=I{JFABRi!ql>IgX$HXu6|L6QUHPnH?YMscWC>=p+J6zGj6eX#~KIj_lti+T~nm~`+Cv)_3pW5M} z$Brc?DFwwrS2s_PQs678l0jP^&wcGutA>NsydtzOfB$wszgb~WK7tBr^cFI)r;ZFF zO{38%G67ewe6YC-n0jv>{CTTiI+e(8$$?HzNqN$=*T=%#)@{PV&(B}s88cT`_>54~ zMn~tVTY9h}g}GcudXzLeZr#q3TJ7ZKj>i)VixdyO%BY%x^2lo-odkFM8&!g|3{a#Js?A;<(0;F4^5xd|j$sbfKXlU=TvtR}V2ZIfD zQ8bbivZ929gqWEFukE>;J=wisH~TLBBE>d>N>iVMmNNT>={;KXW}U8YWk#j@-c7m!^P9E+pp6%4t|I=Wa{B_`iV6xoh8N2Q9 zZ2yLrQvn{6-o}Yt-r>U1LmHm@GP7$%y3ePJ?dAfPk?I13WFqU1ew!DS4m1qw5@v$! zd*1hc#C|HZl9KX_Nll&Zi_K&XM4u2Bm?V;Slqc&#^&zQcl-@qO?zS~{G#hfeLD2Da z@%F^FzetqYpq=~1h=l9dKAgXB2A%wqaEVAzeERY7>uy!!lb?Kq=gBuP=6vTBIQ+4< z4SnVBUrGPse?(XQphMgj$->$eWk_eKwkz9oY;uV1^0{d#Prx1ed0>T$t)p^u9ddMl zL^kFsWFH}`hj5vlQ!pNYf$^)9SmxG8A70Xikk&v756VXK!-v<6%F(-q)_6>8-M=($ z9<@+G$*3O}o0Ib(Ea*tcy?Md3`Z@owvHjEjpFL8>y1EeVBV_vrl8Hq!5GU%Cu5p$^ z8#S2s6l3C1sNi_m#uw9X-@A7~L17#U(||IZi7&CPv4^1nxyRVj%z^jqN{O zjdHY;vqmZB$Ap9q$9&GbkSN-Et(Po}jNjch#%SBAmDy3)7vjd;0n1#JVSKnS4WbNV z;r&(geFDM=yGuGcZ0;9@U9b8FF#w%BYAU$@&&C=$ykx>ed^8oP29e5I4Lz=(s$pVU zhj7w{L(<#3+O|0;dfWydq#_jLi%y`m(UjBa`APTU31}KU(Pn~OWUr85`f&^y{QL)T zo{aKA0i(L^5C6l&vBG2Js`$I{m&`k_dw*NDJ;s$Xka zR_5p@Uv6DyN5$v8ktkyeaDI2R#)00O#7IK%N*Sli9B5jxOpxAe8RAy_Azn`Z#!@E)!w2>k#~3h zuAg@cijL3Z7?U+M!~FZ9hv0lU-#HCW;T;()xwq|1r%pxxE-(>+_P4&RmEOEGs=eAI zJ4B%I;+N5vcJ)tlV4OX%s`0Zh^ixR>_Utq=fa6hu@yC|vMo|Le#f;Qt@3YLC3jyN< zbV!X0q2*~SHT%)R|3so|2>M|83V#*l#)Z_fi}SUy6Kxuyc*>8Li!R6TpTNtH?h4l+ z&?-O(AdsB2gr@i6;fj~Hrpz4Zl-q5z@zBXv5}oF9)lZG({c8*+CNN??0~H_SwUGR4 zltOA#DOG9TM7EBbRRwJDUWOygYWs2XGo&nNH`*7PGYLPn8tw2qIUdX8js2su^t8q(LtsRdwPWs~3yXK@E}IUDJN&G7*e`g=pFx2a%jda-sdtVVRkB!9v^%K`>=lRhS1daA^!A5Xp2T z@GtuRxjK1NDu^-+7?koQ=%1g!S3(fQzX!rNK;r)%0Gok={`0pU(0`pAh5-7{$zUYV ze@=$H0R89W|JjB9w@&^)8})y@#oaZD2bBQ*0m6SDFmR9ZX}^HtlDvC_g3K9$i}?u6J65Eg2FVR)bVlo^t1*Lr5qd4`?h zrVP&GLO@ubd=@1NLXY|AIJ~k^O5QCkK>-3W*+xOPytWgHKdyK8L~W*&M>0F0PY75j z;-Y*3tAa?s^$=OS^}`K={C*vXZ3cZhX?_fY*i&|O-^Q1x3Nz_4mbl!{q=kfp3=Iu6 zf5!&p`Vrwd@w`~?Doq0+QGiIqktVA6!(P}Z7d9LE&?UCGyV@>2IQ9( ziuK?D-TsHMkeRyDySuyeqBO;#h|`FKNLnRKB9J(O*6b%@hCc#3niS#`ytJ&eyoI^B z;##u?LuU|4WC<`DT4Z!IbE09od{U{G_MP%r(;gf0(^*5PX(7l(l$M&CJ5gPqtL8iP z2qqIpGLF9Y=(h>vrT*fDBg$d9VN=AFK__9YK7ryC^Vu*=L-uQiO-(9_;A4xOUy~@I z65+i>q~vJG-^h{Phy94_00Q6M5e zJXwvV=7@Ol3DOC2QT?JxbK2M$4lXa-AMq+IE)Jh}WF@!7BO>jd6=GN%1m%`o!5l`b zRKopR?M90(FZMzxXz1ufm#;UlR^b#>Od%-zd41lC-2td#Wc)5i_q;okIbQrETIHI4 z2jiXbqGu(?Hc3DKbRcJ5FylnERz>rfJ?x_4&%!1GHD}ix~l{-vs=M z&IbLCJ8fq7wbFIy!@$~tTW#cR!Z#IXs@~q-PEKqf{;Y{Ld=|Tz`n+T2LsW(`cJ}g9 zcO?R;u$sG;Gsw6($4`%01%2D!ud!~MJC(E0-8;V@^^YH3%(t*GNZ%yhia&?Hk1udA&CQLwNGpY-TBE$@C5 zG1=4m#`H&=g<#bPrOK#cl*l3^jZDC$-ZNBR6gGLgxfB4$di0Ojo<%E^4!hWy6uVW; zSn=~j3RL<%~Hn@%uWUB2ruImq(ln{{f! z&}48SsCQcM-T3n<${@M&SLtSP+3%|D3!l*xMj^B1FmyboDwC$CEgiq@tt~AElaD*U zv+$$!bkb~J)2Tdcw=u4XUJqF~cP9I{p25(OU`aNamwbZ2K))aJq;6B zI1)RAu0Jf+?zQZ+7Un1fo^27#IqROB`8plZSjkoCH!Pk`!fc@?7ke6kk>GgSse33s z5CGyzNs&+Q{2LDDL_HsL9TgAf)hrI?s&h_;pDz<7U=M;>oY%(BH=;!6k=wI`RdANXh(PXtIUEiwcu<1Gffv&W;=Io;Nrpz>alZ+ z{qPAVC#OvCn;jBlLS9c`=OxqF{c4uG5pgl`SA3Yx%6@a(Ee_&$y*y=yJxE$o$@eU9 z@A!6HpARSz!>>>LZr9E1DGAzqY0r=0NWZ41GuCDwI-X8Y5QFojI0k}xRA7o2FG4df z$YD8)msNDM`G4NL{lK$7s+K^bCZEb?qRx{=Lg^k5!yQ|4^2+;UFHWU6N98U02v^}~ z`=jMEbO`P*&03{aGDrY zY6GL=jsEy|_FjUVI1oWa+-)mQo@-6157!2<_Pn?XXV?Hj&1 z|JEXh)_gDTS_^k~9!kp%XADpfC)poxscI)8h=z{lv&?5A5s)nTbXg%R@OkqlpCOKf zbHvFD_xYEqe6R#?U1R^JehG3crbI*8o4wX5hp3cqs$6~eJp#qwCM3X4-qPf>>G|yX z^(72h+z%HUOHGdCf{sH0Sj)r)Tm$Bu%w^br>XVstY9BR@VDP7HD|C-{mvZP6f4C>M`JNb1a?(( zXQ`762E6+o9wZ?xEj>xb4x56v*!wR7 z8aL}&i^|K2eCq{c?cX}he}mNe9@#rrsU%2C=GiBg$!;ZNCLzBBQ)Yg&&nL1YI)UOu89*hX<0X|ny*;#Y<}nLLp6w1`Gg5@+m%FhI z&k`C7_2f_{okAAd_3P&7$s>rR@8Qtdw2iXc#KCMS7F9SXB7ew(>@}%zgX^qTxel=T z#Z)0*tP;6rYFsAr3rAB|XD_Sfn+fsP#e={!>u8rga3!T_-;PW9E5X?mHeFx22Wdu~ z+W0UWJlXnKP zJsLi?Uioz{a|>~|-ExpgMpSW6(YYVZY{fRM>Nc7$%$yW??4Q3>WpHU(Bfm9m;Pk@ga?wl>1>N(8=JvHcP4U#{$5a4!|r9*-ok|+=B0$nj#*wC^ymg|=%bX^Y`5kk z3&2Das$3>QyFovDlFiOB&YPUpvZCD`EsJs1(=bhY|Y+rwjQo--@wfCqYhdpqAO0>35^@vK+tmCeB< z(QNUxvgn}rcY_2YbiL{ajZIcH^oI@}{pNWEs?Z*_!qThM2A$po80X9pq7|a171@O_ zfHh48amFrZrZjS1k)wm8M2X($QPtkQExL7blab-GP~=dHAB6WM9$}G+GDQCRewNr< z(CcL1ixBq1-$s;-lo*BTaHZyEI8KF3!1ud%?~v~5-0jVRo6Xw(n%ByG!&=~tl=_{z zFi7zevF_eoV~Qj}Yl6TIU1DihR`m-_&b~3;xTEK1zs{ew<8BiI98*;7zv^x>@7zG9 zr-Pvfd}T;XB~j*z=XHyYsU6Gaj1~Oo=;!PeEPcF9vf$S;uR#&wny}v|SxuTs0e6MQ zu^DgwNU%lp&>AH6d`bi1Z+v}6zR>RDCT~A75KA2Oxs19vC}AiW%lu9{r)UX zhVEtIYvheu)*=A)Nk1Yy;~eGawn1Nk6iLMPI<;^9lwpE_da;U8kT%RM`|DS;htB<( zD)?oh%4~(&sF_!hO|}utthe57`qyL*Qqb5Ki$t!zc=5x)wNKu6@4N?_XbRVlNUk}P zI&>{D{bt)tm(K*D0v!&+I6(jap1Z$1DY)m6!>85b%NPgcGIWm`>uP?~ zW%->iHsXoKW6ER`6Uk@xrzFy#@k3;M^z-f`R?A4FON}uHmj)tUOu!3hI#;DzlMRst z-iVg>pGbOh3-g=9CGHQ4^&2Lya=5)FMz)w0Xz6PxJP|1XKH&xE)TYS<69XgBN&o`3 zw6Y>td2g}p?Zx6B|LY4*XnVNPcNt()3ct(&i&@s~%_#a<^mli3Oet+sX% z)Y4d7=kF5BV@Wn=hxzdsSMTV_a|f8kELyd_Z9AE4r&KEn)eRTX5Gz ztKvX#+9OMyDJn!w76&oCh**jqCD;`cz@rRN`uw^fgcfYo=ZgmfePe^b7Ois_buwti z1EsJUH=1sQj8#AI^uZ9u`RLzDBcBsb$oHpwV<1dSX_||h8)5xm*hB^Hv#20qOm+%=mVvRp zd=VPHN7V#>~Wea;GxIx!MZV*r;@@-VIvP3s+K)S8| z!)FEb_PiCP4ontf2Ulc5(8H0pZ@Hw5s|mnhE~_p<_d^cnZo}eU0iT|~^n$_&Z@wM6 zZmd^Rq@eAtO&y>^P1bLRH_P%7#r+Na6&-m%me!VqSqey)2{g%!Mg#VByKjVi_AP_k zK>T5!KG7!2=Ocb>69L}b3@<4-NuCAoHPOVS*e()n(k|4xL7&J1C_bUQ3SvD#?S}j-w~LepoO$^nCA&inN-4@mf%DsOSsK1Q{NCla;}suE(3kZ*8Vd zzV|I#%&B6td&9XZoB~^8GZnlJIyjR1Go?601bJ|x9;b>(vMCEhuYYNb`^DVv zw`0-S?CrupWNI2*fye4VGtVt_rRPg6is7Bp1fWbzn;qpW2_&Q0%>?=O+lOe4!0g4M z*-*T4&A;d;7rr^gIfvqC!59XJ86UQ1%b_O#=m6dRsoPLW=Vdgi<h&mJ|Z{bLAsK7SA$TR<_-89@8VjE~cV} zE0-W_96xwaBghhE@^Pyz245wcnVnKpB84`7aP#6$kuEWxp~_!JTJ!dmwW_-jfkcUV zX%vlI7)0KV0um7up{OWt8AGhVyllFs+9FwK#hf}j3Z&iux)p;-$$CQ|Q94E$Qf>^1z;~dNWA!@OrS6D9J5C;bnH)iBG5B4N@DH zx;j7j8$`WKlTm_O0a8wP#ts!hgo3wqeZSCJb?7xAAi9{G(wKC#$f&ugn`a%C?~wHEj2bYDG9E2pmI(ewSRU@(^J zn5{5rufla}WQoryRr)I@%fb}QD!Q=zmC3^me8(w^f9LGu*(lUN zyT)DyAU$2uUr~lJw>y4v9aPEs#<@B7yV4+zgxj^*D=eR(=~u6UIIc{h(Zu9%&eJq| z0?k~xPJH<{I4sl?5ue5VqLmdWmx3TYga&QU4=wUrgch=HS#C~z8D5TydR68NCCw%5 zqVRl;IpslR5{KmqwM06lIC3HA?XDSUcd_LlG``*pN;k@-v)->`?0>o8sa!mL$Z-ae z9+Z`#mp1a;U3P29XK-ByDB3IHSyU8vvGSE4zuO+yNO|0UmQEL^ulacu$j1IL-@GPY z4m|xS0RmDbbtfUE9?eSe=IYn~9to4#5en;VrvebAv3`6tiLKYG#GzB-2Vm=2;vfPA zrRc`W6B0n2Bc?)N{mCxGOEr_G)C1hdR z1h;j~n`+_8{G{w7Gq2HgS4yQB-g8;i)Kg)cZNCTR2p<>M|7@0^u4uAMw;_LfDw@5; z+ae?U0$^!spuL|_F!0k^t^Fd5aQh7QayE0hS9UIZmXE{2`ryYMla?thpQZSZb1fkd z7r$lM!rTMjGv1z9KpH@v5CG@;*4wlS7ufFuImmr`EbUV?KB9O8>vw~>9a(?QOgno4+MCl|y8&jkeqWB3?0nW}%-Ty|Q{ z*~ktgAr3Q~2{d?4!ofdbRw1cHc0f=~VDq1qP}7=drT`C&RpzteA&4i?7MOEv2NU76 z)5x`dNc$3AoR+QqtPA*KwLkPCEv^Fes;d>H)A32YmPlJeRQ8$J4+x-*=Dz2_T^5d{`;Re%S~R-rPK6C!Q!^M zy8Y*PqgqTDxGrsk|CJ6NA}6VlAfOk99qyFiqG;_6bIPhv{N`j7!PsgPP!RfqEn-)-Zrf2=V3-(maopOo^u7C)3Tu^BJ6c4bAW<)ZyD1ZB`6JQ; zfCj$zb>eUR(NUZ8n4-aDALFftRvOVgucJ~CX!AV$!ZbCSlaXWauP+(?154u^0eFpn zBbiPPQrDf8fjiz67OsddhSj|lF^J&V9B&Qpwk*q>pM@}QNp$v*@8#>3Q&^zWc(BkV zY8-;N+A!7}DWA(-5kd@+QFqc$11E7p)DHWN$!N~(Af>?n)H;CH_pfI6B>Bzzzl6U3 zC;Hs~o`m?nq6+@s7cT$DQUCvyOYdQjGwcT&&H?@%00cZ6e9S#7J#z&_V~N6)v-t89 zBbj*P1GJ{I=9rrNz_luiM~|JrQF^z{;xikzp)2PDlYm#w*Smqelt0%ZIm8CbPsvLK zqMpG5J2TQTWE9&wO&t>qPjxUU=v`|S@wk^{&_%|RbkmmWBVHlp=1czNnc2<;;!Cei z^p(#&EB+bK3550(udWpy=v$IoEd_et4I7!DI@o%%OWxRD@iCBK5GWT(2Vp3ujFHen z6T*Oy3nJl10@T`vN1v4Z%{SzCvIpSJ5DpMxS1|KiF9|kSd*Gijlwf?!J=5HS7$opq z^Jm#KMQ~mcVQSW1=11jfBT&wkV;p9m|7WVDoO-8ZeOKDNY>3)Yy=j(ol%o~ba|{A~`BMEI`Y)OXafNI-=H zI0$&Iu+a7X5soQ}Fz}KwTYG{p4rZEVPr_}n**uB%b5Wmjv-#}3)bP>8u3|gTft6Nu zj)d$qgPZ#3$?w91ZDf||CSO`smd~(ycoNMyfUq7#Hj(e7via8Guf*@_h1=eE4fFhb zlf7AHKDN)LO}btj1IRci2;vB|CHId|HQaIQI@3bg?5z_;Vz&W64n`pZX-zXwzEw9P zs$Oh!Si7yJ|E9F9W3rcJ$QQ}@K$k!rgS3&^%i0t}WHEp@-9C15!#Ti$Uq?cPgh19T z@8xCIY>W7la#sWKDQMV|Ye|s;ks;TiKV&$c?M(v zMS)ZNjOM_qzsWSu&1U@hTt=dJlMmf+jLvT zZm`%@-QO4-dFc$0pk?Oh*!b|c7lAHo%Fs|rvwBSuco{3xP4XFQN^Db+V|puEXCj*$ zzI$fTZTgcYqch_{Y4}%U;BBeEjx<%VN~YcOrN+BBvdGxjth>>e#Vh-jnJq4-J^-n{ zizQ`^QcwH)E5~106|&13?w^SSdRyf=GCXs5=%}SdiutZIp|HXXUsXbfoqdq~J64M= zjyGh2L}h$rB#DNRj*hO;@+D|0K<8On(IzArF*wt$W#`v)hjILZBjamgVhyf0psG72 zU>*xY)+esN=ZKxSZVl6E7)D}Gp{dRJ;7RV*zN{(QJAt0AMkdlT7EYPvX#J(zlmvl8k&4px%6X|X4JDS zV5m5hT0uHfWpwvKJI7)$o;eB|`~pV!v`~p>G9WRTE!bpxmhW*eXJcvkcWY<3mXV38 z7EnsC8l5!SBv^k2l?J{`Hv1l~Oe*p?-n4eOSU0$NhKv|YPZs!4*#tK%&fLkw3BDBx z?sUFdstqWwvRe=k5c7X#g`9^MD8{}qoBZ7&B}&c|l`wa3^tsgY%4Q0AId%W}L^rHiZ7j0M zwzlYqye`fFNgK-&I2kO}zyjVI_(jZviJ_rkhIXaj&HfhH>qi%@2mKBxNh6Xa9p?RT zix!Sa&Nm<}m*B2b*XDb3tX1H7#XM>?@XfR!{F;nR$mwBVoY>9H4S^ptFLiiz@OF2l zTxZ;ux9W+BdHntOgYU0CAB^pttu3Nb>Eh{yT*d?(8d z3b_3Vk&$I(h&8yf+kE%%U%UVl+c~$RrPZKmUMi8}PxT&A<2E*h8Cg?nsmURbgU~YO z(`P|uoiamROSM{?lkCFLl=SiKh=>Sr4iK)elZv)yhAjB z6W}j5rmq-L;bsHqD_Z&x?dXoU&Hwu3T58aio}J zqPBI5gcb`eL|Y>%kG(SLU=-Ti?>Q<>`YciTz&qBSYi9TBdNb2eZdV7)ajG)g}I$>YiKiD69J!mwR zq+TV_@xp_6ro1U&Fz$SddD-uzBX>=Ht)50sIb0*fGGq37r&B)@XB9RXG`!|d4Gx)b zSDFQhCrQKB#N6k9080lDiM)ob5~5rMNcd3!&1w@%jOc7O)w)j4rWIQg6wz;<4RGWM zg$i_WIX}0*{_bDek`;ok9-;ttiB@JY69}@5TFftD2zVk*$YwZ!wikB$XHQwh_^vEK z=*DaB_tEVz4t|{g;mbFh5|7a5iiJ7?R%2oJpI>a+ym|;hdBkQ7`cjgT(rWb!jo#%@_RPoh0H3C!}j9ooRIq4gIv+57uTt6X&T2ekJ<2NDJ;;zZGiEiP|F}h;u$}8a0Bn{?bT4%XoKDM z%$yYSv{jcLTWNNklOqKM0$k`{cm8p@x^Zh}QA7ju*MTODt!qM$Qc_YE80TVlw(A(| zUa;D$N%rR2u~zp-1b*Mc`TCqdY~+pX?VHOz_=~xXT14f!tNesdkRFeS2=wQ>LA#75 z$12?#^}Q&$F%y2|hgxdm;^lVT*2wm*qrI841rOri?_^{EEuH@`ZqaL2UXdj&Bcm<= z9#`*X0}Q;wuKOh;L&#%4N!5k{rjK-Qo`v9&)$bWKGMJCUmX6}LRqk$tDvw1fkeQRi z9+ltgl*40YF1hCoVB6bQ$bkK`>iiq_)O7J<`-i2*GOhAtmc%Z^hmi~qz3zZ5A>y}^ z#lBPA(Xjm&gF$bo2Sv$+{U5^6)YQ}@Wb8hk1iadxD^5CIt@GOWBcrDF70{*wuCC?+ zAFl}Xk_0w^b>TK8I~beE;do0>Gc`q)(w7(#4bdi0r|C4*@-ujTCLSoG^H~^ zSU~4`uRFp$={Yer<~|Rsrvi_1ZCY~$%Pq@F{X4(K1)jv=nPQW*-Q8VnERi4&kmTEh zT}JczK$J-QzxeG}o-l43<)tIfoVB;7q4DWF&J}m=!RY-_1Z?1>2oQ1*!gTZ0v|mjL zrRm?#l8%&7-*s8=yr&sXs7Iz}N=<<&bP`40K zUXEyRpzlz_<72ZpCN^9kl8Uu^k6x<|3uT|GYJg>j1^;ugyu1vc@4IOH#=yf!{0}EO zE3Kw@u)FQ0EoL2QS=oF4P5?Z$pA8TvGieGZx?0cdate7K0~vb0#bHN>`d#4lo#{lH zX+H6s`rrQACEUj z8ClT3FV&#QIW{&9Co%@> zDWLB3yGL+0n!AdAC2C}ouf*T}{Jwg%gI3M8#SoaN<(;FqJTNxHOg;<}{^>LZGztRv z2acbQF+UabQxfEm5drCS&pj%C*M+}CQR0PZSMmX$eJI43s$#sL5&;y2=LQ&Cu?sHE z&|wucC@Ahn3F4pJ*oH2nu{v)9j?!AqCJ=)@@GgPB`1QBvsIo+ZAvl&8(x?b(BBx1X^NQ$bWi z(02^glA4>F!+xFR5|I+P^jcV==}<6<)+#-0rH<}`Ho9WZj{BJjmF_}^n`1a7iUjTgb8sCY7Wj`KF$){69( z<^;@2YN|CAcSwkl}~F>x?&?Rnrl z0`#E^O%iOeyyZ$uON+psX{(@w?C!FKd)sa-fh#4IMc4N7rEZXkNP&u-_^aa;SAG$Z z6?0CV5>?77&B#&Nd6Tg=HQiRnJ;2fd1l4ZS)4Fb{oZ|p?pW0JQ+y77L49%-Vl;FUY!criy#VAEaDW;|`S zRBF(^p$sut&&{Sq#8@o}WV&2ZGk)xV_y!aV`0tQIf+eJ0gn#dsCxe zfCvV1u^aw79*i`by6l4||7>QJ*?Pd80Ofq(F5*BZAYPE2Vp&9nF}v9A zp}xXA63EQX&Nfkn?7I*2_iL4?lQFc=NGVd3l-SX05N#%XX>R6MC|StbT4e-NX`gUxX zna!4IW3a#We7yR^ze&-R-r%6Fu5Kx3uL^ro=_zkm@Rtl|= zMM(1TLZ|an4+>^+=$`*fDmAlqX)bPwcn~)k4*6sMPc!H11BVzoL5~N`20^W~&;I}b zZccGJllW{Nt9{(!=`@osYx}_|C+!z}IzDRY%kS%pi@#6adTawv>gwu>r&Xg@2?%Id zVwWyUsN~jQPf2WprC`I@*eyUv+3? zV*h801@MaJ4n90f!)q+w1%pid1^o9_G)O()MT|EBrc!R}r~9kFk!11VoG-%q2K!;p z$3gFMyrnO8XQ(QSo9_SaX()&HZl2AQ8{8zSpr()0%@*J(McY4h8HxC{&p&q{V~Dk z0)E$y<){XJ9jk+jB2Q26kgB-ZQr(nMuuoC`0nB^T#emoGSM%_gNuU{eG4_;Z!13e- z{$nEpE$!^d4O$4~%^UqD<&_Ciq2#Kh3cp(`l(q#&!GOy#gU7Yi4jV5D2!(iletxUR z-uNy4Quv<$I!dFwk zs%&RxM@dNu^uHJhiWxL`TFn*^UHtizsNfDQnue1Hn%3w7#)97!1_qT*T0bcWRTJcs zu~BM#8|=r^d8YydL1r;(7K)a0!9dj-@sUF>_Lf>Qsetg zdSyZp&zGAOesjk;m`}}tIS6js=$c3H6+FI@Zsy==10J4 z&&f#+dwvc1+1^TlPnTa$QSj0@@Th?ZHt}9g!J};j#%iG6sV`8#I?9hz;ohH+Tp$}S z;9?4!Jkin8O0lY2Fl^JVoHO>T)VrV?c>l4$;BI-(?E`th{E=sdVv$GrZ^tE{c9Z8P zzyl>qP}Xm5_k4?kB=$69$>o-XfiHMEt%A>DNNA45=_&x6Qn}cuhD$5T20eTDWcD>L zu=#V=$K}hUOZ-X$#GthugLYH3EZGJRa%yp8Zjsbw_i#A=FuS}90wW`N*w0}ihj31D7Ro0BK1MlgO6fV?0bTV{ z8Z}6)0INI)Lk8_Yn)q(nn$BIju^R4Dy}ePBT9#?BIoGJ^<(z|RmE;17+cv&z-kV-Q zP+rCe)u*s|9d;8T?|H!ewtwc!q*L~=@G}oTLk0^MQ)DLwL%a3k__nC?m5A$Q1dW{F zblQZp+H#%qNju5~x%18Zm+nxh-b`OrG3cM$?od=fAddahe{e<0?@a*#<2GYdX?-m% zv#%&HP2wid39wO2*qYiLgo>OwV45aamCwpEDk&-L!L?!MR--yq+GsqwE%taeF9FM+ zpvR#lCTA&`5^jI=$@iMZ4~tcLZQ5$d;uJ$fsH9*>w|rC8+V5f$7!v9Y!Luq*BxW?Z zK-<>HNV&XSq@|myxIjI@+(m9|t^=aLq4TChpvI<=!;k)i-5U7()al&8>$hD20N}m7 zJ-_QCrHE*5CPXcbcoLEH)YQ2jW(CBC?@M<>O3Khl#6f_=RU6i1Gm&kF8=qc;fRH%c z{&Bi!!Oc2Qz`w8KW=*4AHpTKzQGSHEx}(+oPQ7^AjxP;Gjo<5O8BQM1#Sd^%#>90W@q-p)XR&c{T-tAzn|%^F3Fj@j zcpVliZaY&lF9u@Y06?d-OrCM2-NTK&fqvI=J)FFBzm24M*z!+|1`{r{8<~=pS4Ist zV_X&lR7D{1n>}K_4+*r45O9*RvXd$d0U_aQa?SHPLh2~6#pLPfbxO2XcaJ;we?|O* ze?AmfQcQ_DcV58L*DCQHbam(dM&k75OYySfAnR9{#G`5!-Y{1yJzKSoTWjF=E;TCOe?PPmpOuSN^@$>_Z4*&Rj-ns#oZyd^O3m z^H^Vg(#r?ipQ#1m;R4jLTZlj|k0TYjmW(G;2sBV{l++W+#mLX~odp7Aqb>^y3JO+J z07Iv{;n{CkjR$zHW#u&&(A+Q>PmYSH#jJzy3VrEPThMH*4}kjXJ?Yk_bqnpr)n#_& zb5#q?cGEjKul$M>!tOJorh7-#YtVREq9QWMRB|HOni~M}`P07>MJ`P|SHr zTH-?lVPIew8k*o3VYiSMheDwog!SK8AL!=Rd44_Lf0{Ck;hduRo+SYHhF=3bH&@1Z zEzP?muubO9_TQqSBIxDtAiYX93GY2~^CkL0%z|U%aAIe`*3~K{UmJ^Drd!)ec5?F7 zh(mQcYYM%PXr9kid6lFR2$c+ZGi@O1l`vROPcNUrzE!)^u%!o< zF;v=Xqns_w7)kYQJeHW3kFM0j5T=#yu|K1C2WN`zuk|?49qC8t@8X)*gPe)!I{sGi+L{1esPXiuv_h|9OHlxsNg;g)wgSAr`1{ zu=$I^3!)u>Knf5w84gR>*f1+r&bfv^TG#pPPfjxw7RUz5FjfK>lL9g}JdXAv6a-Av zNGfwRLlkF!qG2VPB0Gbz7X)B4Sr`~@%|?=dl4h<{Lx_E@!DqBcoWr(h$?d`wmd*3= z?>swie0F*|6F4NF2_TL<4raCv6Fz2#L4Lk>RsY218+;+sRmlIxYV^!D2D!^xr$T%FD}JSXcl+1(C=xbAbl?jlZ$1^=F2tB=lHfKBhN0 zzIXO$Oo8j&aPlBH2G$09=2)xK);$nDy6hX6qJI5=hE^H10nj-|&<(gfIXKoK0ZN|9 za}%?6iE0~^b-Wlu9K+#_1h92fV4a1D3W2vzaI+1wckyKKy`7vow_1h1ly6)g><2L? zHOsVvQG5V0-FJ5?9P>!%b|r}%M8U`BIsau6j1awG-k@K-SnZoNiUN{p@xjb||97;y zQ_%a?)LT~?m_wj*L$^j!W?*5z!2xJ!k)l-~gcFhi-6E71Bs#O5b3LRe(sDV ze(QPsvlM{jNna|L8XwOOUh1W=YL9-au^@ATe;Iacofz_-2xd+_VQ^GrGr3zj0Uj&W zN-t##|E7T>#Tp5*cp`ITV*KO$5mzDF*_n(C?6ntycwtmBja+_`wY4>FGkQ-!&$kgY zh(%<)!u1zxLmsqa!^x0-44{|?T5G0QfHhB7{EmJ^g8IN7${BVB@OSY_^y^_91P!G z7>yA?K$1fQ)Srv9m4NPJm$sEGLD7rFgK9ykOjJxVZ+Mm$&{{}FiYH>RU5y3w(2jrxY>Bue5pu-kUMy^?g%tnWfV|n`LAp?m0g5wx6xw-8-npQJ@Ef zn*?UxZ5sr57^Twet>;Ox3~4D{`F4a*?6w}e^XC^7L_m|df{?(7tn#s(18Bk3vT=d| z+wR`p%}Ynn`{Cj>@684doB6!n@Kv9amELeHfMEv&_nq$FSfUTo24G_n#e*|%vSwI+ z$ybw4gJIms@x!B_}^c69CQjLlHxaYfy3xs&tN# zP>KlLD&E)PEe|0a0}^4T!L@&GlC{f$3P`71|Eie0SY|*L^l1S$7Er3AGH_QV(uNQ8 zn|(@ctHYT4g4Dcn?6n3h_KV-K?5=mXshV5$e!Pvuv91%~=5{-{{!loQYMF`lH=zPD z&8BBk!ZB%EWYJ0T)?vc2xc4i7hYF|bV$=E7B_;%JAEDGI)_fVglD)a!SA$*Vjr=6a z0~6qo^{ab-<7$zaB2@MwFb0-H3&nK&Dp|ABjaC2DPN20(7q9C z5$r(rs;#RNdpa%nJicGJa*OT#6_J92Hgzw$Xax>@BS`!HWG0mSm` z{oDH9jrkgfoiU=iR@jWv#V(exw-|^)^eWZF1{|WAFCAGd*cdL>6iB~gh-)h=J8)`d z;(jpIf4d3TKQFzH3k5E=^(%jOz^@s|v=V4?zoh2a&?sB#$+PGsKqRa9{69g=S#Y%L zwXqO-c%S-Hslk-pra$C1>BN*W>}!WXssIEBs3hTQX?SQj=8MqouLZJ7Y1bW`_k|pa z*vP4)S`G5E0(CY5)FcCx$4ba%s9-`|<}zd3KtoSE~^nT@-iXRmdy>%Okfy_)lKR7E=HFx_B zoL|~{RNHrH!#;9Tn(NSn(v|LG+y;L!uw52YBESvvT=jhc!RdG^jdGQRId2=!Q|6+JlLoTJ59A&$zl&TzJfJwAsd?1 zI(9F;*nlta1VY79s%~3t`Ym@Bb%Q$>M58KEeGJHRnPKLKiu#H8^89hF7Ry8#quXa* zrCJp{|E82HK53E$eHkTs^|)4`WRSBJ%|5oxoH}>CjzmwL`8n>$Lm=zqnhcW^yRunh zfx+EpJFP?%9D(E$B-v?c7f;Z&%Pqc(<>FhWrzy}ZFx8PGp&rAI7cplLn*Nih?7c;z z{vrN^IOa64g~Ey&EH<%og`z&eHAPp)Z0f0p_jFD*osq}xuzX1YRHw0C=J5)faLfZYi69p7@> zT&_I81;Ifk-I+o2ImSyIlWOnWK!(1MCSefy-y=*QIRO{y_C^UArr7trE)0<@(2O{( zK0VAl^oXtZeQMCU3S#*kGQNWb4+}AF(4MQ*^E{s({Gld`A$+MKA@O*3d4Yz{Jo0-a zIx_uqQKh>*+MH~D-{ct^ z;gsy`w4@{z4s>KQOH1E&((vUx>-3s&NXzx2Ay4)5+VOke>*E@2Ch&tldFz;N=K_R9 z=~zBWV*gdIi-n!mzjlpaFD@cg=2VlD+InMp*s#)(_2!#c00@Vpw@(_pF4Zb)v>)&& zsskxod?g}}vXC$@q3nFF7Ew|1uar9~T>FX0DmVr>Ik&z~;*tHe7y?rzdOJY0_%&p2g{5?t|H z)VrsE*a5i9ec#`l#`#jWmp#8e2Bz;j@o_1>eZJfIr245f1qLQ}?czSwFm>u-cPbdf z#5;B>eRUfg(!OKnBzt&F=J$c^I^yfsw9BJ^x6QW!C)YFnhcC7KToNym{c?i+y^3-r za|a|`W==plGnJP-K;6vzD9-2h&q-4=d1}wicd0_Rkz>KEuOc%4H*CA}ru5culBm~i z?x5Ma4AgET0?%Wiiv*MAgrmta%`F*Is0%OtOEtHMNtgGEqUaD6B8wTOBo5)oQ zi)_0-<{2!43rF`sYrl@bgrLK7DgIZ{Qcwgl>SMxJI@b*nlaRRXUs}tR7A~#9``uFQV+T8{%a*Y*DA}wtXEs_OZo(JP;Flfl-iWARj)#awSrmxxy+#X(>PwSf7%i% z>h$gbT@{;z+ixT{?{fuS)WAWBYDa%)%_e33J1#P8DN5v!$xhnP-So=ReRHjBcDz@J z(aM(WdI~fj&xGCp(-}RjpBVIpM&Vw7iHV7qwW+UzSvuxfE|JP$G6pg;e=Uzyh)t{9 z+t;T^AXq6rb>A;2w9xs4#@gX^eYgM$@o!mzvz0n?=XrRo%FuE>xTmb@7lh~gj-{{p z43YT+7jDcJ!{BdLAJ*U~-+0Kre+5|sWmf2SG4hEYpG<92lgh{=DGR4>4VLE~NjF~< zWfkdvyMK%$@TJ3ZG>Xipi`-ytdX6G0Nn+Hb)kj<@G=t^OEhizlXFa@_6h^hIyB+$5~oxQwuT#njTwi67S#3L_gN*rR)?dKW(jL zzXu(5?J13m{nl7qR!bC$$b70KxyY~IC~Ve8^E1DM&Qnspi~dz4E)`rx<5xL(k3UjR zzPel?e?uY&PQ~C5N2~n!cfB5ebxSP%M|S}dt^ri8&{hGRGyZ)(ouRjrX?l*;oB!Mt zx4++@hfi&uy&}8zPS}p&t}?7w!{*GUiF9c4-B+Fx5L*5;_L%0s8gQ{^nZ5sTopFIb zV!d3e5g;C*ZZ`eHkv2!zTZwJ*07LyfGA^sIMAn#d!N7F6DtOBIqiayZ0Bd73zI+E* z65ldiCs?vdDR` z%nS;AJNdHJ3*|ug3Ql}Z|q6QnDMFKfAiBOs>q?x z#%n~z#F)1Nw}W?wHw*svC=?uV%xEn_OS4xqtRBQJ=AxEoziL9&>K5$S^;jOp?RboY zU4I3m{!7Yc9T*tc`S;HWz3t|ZRLE`jg7w<#-?oXFrEc32X(a`RS&>$zWD^Y`-P;>=i$S>pUVRJL@ z$aJ!2F>+{bR|K;?k8Q+^=ClyIX1S*ORm0p0v8c13wzjr|p2JG(S^hpjV7fS{M*?<17I;-5b-=ayOj3EAtW^CGQ2Rj z^R8^4AfSD*4G`-uH-fD7MUp2hPz}3~6VNejpah|_PfmWy`xF4pVYdLrq!|;IzdL0= zAKkaw=n6|qLEmseqFf)UO^TVtM~q(FlLp#}>WQF5fX~W=swIaj1&c3W!H-D}e0yH3 zzhcZPLVm5KyW0voG|@#$S+c048>w^=v?&EC002v1TOiaQ=^D5;hb`*HGI$-IAMe0R z*HD{)1#P9s`~bLfVPGP^Oum)j(qwF)%uKx|T^s zMoRf?7$xQ+J(Hl#s!h}xyka>6I~i|aCHkf*3nQs^mrKOEaJ8QdstIU zs*0)(5r>${A;^#}gfDzUKIQqY;lWH*AtF{wJqgQZ zx)C0EQvqcA6Il@%`&>#}b7m@xEcb6d_J1R5@;*pzZx>RWYZuAMrvqX11Dp*j}3Yc3q?f>>ICbq?l9R&tB|4@-8w%f)s z`3*X47V>p|>j%}>PiJNl%fFDHm@iy&s3mHv!N7tZ3#CscUtssqxwmM z0{t(@qWdRuV?|pIIxQUAuXTIwU!$U;9#ULmAO?7ut*TDausxX|s%s>6Upeh+{9%)( zmqpP~mztT*pFLS^N}e=FNJd8w=lqX|gMs)Nv<2E$o*Oh7{=!evsJO0sy;Yox?7uYP zS(^C0^dTNT6Px^R%oX#J#K(9{tT&{j-eEs~(iKm6KLxAl2LrN*tHrpVW0xSLMP*NIS9&UN}Sn$euS2tmTGny77gnyQw)vN)_rI z5@(6+6d-&k=>{^q|F(#nKhzL3>FK*q?C=X&^|79^5w3K()=_X!4CRs)9cls z7Yc0R0eO};8+8~lC!c}9HGf3hyn0flo(F7LKeuP14T)rv-Kt0v2n?zmI>g8!nK?r4 zSn|lB4(+Bqm6Bh9W2+aoe9u3|$ZuV$YTQyC^}Wbjmu;^|vo|p0ltP(msPF_+q zQfQ#LR&9QetO2d!DE(Sp&1Ra~IhI;;5>vB^2bqKO@$kT-kRoHi;d{J`+v<}xl809TfL3P|cB;xxPD7>ehUON`!g)A-+9{gHwy5{Wu@ zEy9ud->w6$g6U}e(V}NG3GZc42$ zvEcZL3ATP-fXKuydBnXb};x@ROq)~W>wOlcD6!P%HV9Ym0vbqG*NV+Xce2=6}s zj)+(cv_)QL=|-FddY;q(@u`++ASEJ4z+JA70hdKj zB2f*MNWEN}fnOM}L+@@kEnp2nH1smeBdrDy%B;JRLd$dl`my*e;`?4vm zbZfOX;L#?$hT$i2Sy##GHt8q8%;T4U+0Atv?UcxBBywd(MA2(e=RC$b`29_HupkNg z=EqNYuD=w7@3QAsyxmkVa>!zPh@)Eq%qDiIX{WBD#JA-{(@h$@#suc50C|oap22j7SxZCQDJ2HH~S> z@oa@|py(EosrpXuAoc-osikwOINSmyeSrDn9JabfgEkYuR#m7W1P=dCsaw^=k*ahS z$K;ANEHJ!+afDs~O&2t14tVWu59`JO`l)i8nW+W~3oDxE3=}vm-7z(8p_qzOijsdD zO=Z{!xb#NW*VmPb>u$+UqBhkCcq31se!{{S|rupM6}olbR}(9qjZYtd)z zPL~$3PRd9L)tS1v!MY zk)kQfG0YfD)c!TfegfJ|Mzfb-xOl3PbRw<%UZYfJN#y4&0~oQOk|k2S^Yyk9&)r7_ z(AHc$y1*N8{QG0CZdp*{r*@}ig)X$}S$LYT_0#6Ps?oQ-vyF>dGdcdW`?K6&k({}8 zr`^_-M)$XrFc?lzgC@}LQT)#5y_2Q`Lt4zm54T1x+eC>UBKL|3tXUcl|~LQaEr%Mcc_iD|R!cBpT z`JJ@i!o?Q;D2@F7UcE9R#thf!aB-5Y|InkYhX20e1d&-x+GqPMpiwT?QiIA9H160b@+@MA zE-Y9laAHt#_>d%0D+P(i+D=;K;n`~xBzC@KA)-H1aR^Z|3IhC(;qlA$BJQx(2U*2C zlirBUf!M{PYhcdBZxwYcpQKOSEh#8A^g1g9OX!#IFz=3)QjO9?MlFZy=$5V^6%%5K zsd`WsSSV)HLL8Us*Zpsn+JHO-nr;AWba2)kRn>8A<7TFy>99S|zPZhuNw)vW-B$>_ zVDm}mgIJ#dIXlR!+N~%dBPk)2K3oAPPXOpAM&Yr3i;gDdc2L-qjybmVuVVMBGmk=& z{zhsIun#8-7mFK(_N4X*s1T%t_b%pJ$~o-9AO_fF-~{lV|1eAF?AZQ#hmil<-o})J z$A%$kMh$%bf~e(vfTX8IthEjxCGk`p7td{93$YkAD~$hPK*Xv)1URl8!JK$?tW-}_ z_ulq`dIO%w2^_8U+T7I6_c$$ar0#$>`-`7Qgf5+xm^~$C>gA|eU>B>LNsP@5qdL=g zNV!+%`t>rAaezmbKUU@4g1O7K*$}9+Rpmx4Es;A;5S$bc>hUfBG500$DV-;aI6z78?=Fl@DO$K+$pbXf+Dnn1ntjGpyd z`#nd%s4SVuMZ9p8qi6pWn@~(5956y^68kOEVY!x@jqWe0TJ2X+h36Wuc-Zk|&_yP6 zX3yW}B}0sBjeN*oH=~AP8c#ge&~|3?#j!W~jvpIYj@Z16Z015N!x(K|gtk(ikZ$yt zHN`R0d8`qMfz9~2`3Eiw-F?)t=~epl(Ur-#vglhZJR%#8v`*qXbKvb6)?$9RXRLBq zX zg=3Hafz|>8WpHS$wQSEIR`{9@!$sn&)6qvPtMx&n*^i(LFBZlaRbYEE(9POLo=+D7 zh_flsraNKa69Xhda0TLzmkobvK^;_30KfJAZ~@#>z#9z^8ZeOPy8JoAwBfdM0sL{8 zuXvxH+YzFYnZA9Co^=~F<1n`{zY2KL=;td8i-M{K-#-O2Br&jN3W{=#4B|ZN-#8rb zI%r=#l!7Kx!9cA0!L$BP&iXEcE&{N3dwY9x+e~{MHE8h%jir)*UG=}|Nv_b7OQic$ zZsM}>Z_r^}REzIoy_-VFVZ7sJ$yKYaNC7AlkGDM-7{o%U@yP&JY0&A?m+pSh-7R6j zq6JJ%Xs<84glKf0cRyngzwuZOe}EW6PRZm~oTTX}-XpzUX!CSba;RjI1r|F!HFsyX zuXLlub3YB9f*;7VtF3~2)^~5l7CVD(I(<*DrPk*=O+Ske#seoqqzS-Mg?}T7ZV-uC z^_KK%F0uwqY+x@T6?q(P#1N%JD^jDDe|}9Sqor+T*BgG)v6^?m3DyqF@of8->5r@( zw;RHv8G=W7kyuL|p5vh?*d9jXA!j3PPWyk`5~TJml0hj=)=QtvqNA zc5)YFHDC&*aaz76 z5}LPY*a9clvw7?p8_iEJ-+-xiZpWkTLf<={4upsEJ!mOH5p-fxe9D)r0|x(4Q(S-l z9FEa(rVPr%Ap!le86WC4ISAuvS5e?VDJjEvts^J2mq zb~R}00AbHJs_RT$2&%>~E7dwrpWci3o;=;uy1I;5TmW7(@bk&y{QRDkFqLcgw4M`q zjO@A#LxcRI>u79rlzXrU2)pL~(I9iNw4^U*4ZKj3??rN0MMYQ5c+x6(n`DJ{mXdOE zQd00=Auc=cI$=t#KY;-Uit>v!uHl1bAPf8p*aP@C)pJA0!Dhjdm2-4C!U@SeNFUyc zH(y9P;qWl&OjVuaN*Sh(Gy%5e)f4D`-V^YjU^=zvOI>8g{Lq;EEjRQNfK7`C`N=C! zC)jpEoi|qwrz{80&Edr`!-TX*F^vTnfqF4rJ-J)eDMyD}^^nHi^lRq^z>eG7+w%?0 zC1lTJ-zBPQXvE0+B&z-Iez@4Y5p7@D88vA0L!Si_|1VJ?F)yiw6@he zXdDG*-kZ89sQ2HEv_A?Z*bV*u?X}*rle0gOjW8dkey=gMUYo#+n5pl*9cbYQzey4u zWkIZUWourRxVL(86r2-)MMNZEu`~~k@GJ(cZfpd6PHV;>6sI_4l3!rtdo`24AByFZ zC8Zk!|0k*J#3=1nr^EPaWP#Jv)Ko*mT_7u=Gfl|h@pj`QYqYhY`EYVa*}_RYlMAHM zb&%;b>E+%OiNJrfGrkI!q@e)l%kQ{%par$%vJ6o z4ml-Xn##Ycrx#U97viVpp4qwRPl`apKtn=jK5i3b;E;=_${58nsS8F|D!!O8th$8@ zHdX=c8v!;5daUr@N`K8Bg2>wO zr$A#E36Dv;9+)ncmWZ_iI_=_nB;MQ3{=5TDeP9KJyt;%G*Eam7m`6x_R<_h`;=UCVYOb-&zT9g0K+0e}%); zFPEG3j?fp6zM&*wYV3b*#;c>Ir!RRjjcp3tKvBgJ506Ko1}T1+~4iAqJ^wo-0ja)_%77i^*1oQ!OhLRcB98Mo4UK(cG7tOddsnl8nAjVo^mKX zk9?4uYtVnHM|oN~e7Q@>kqv2a$ZCDdTWKm+^EF8Uzd{1a`v4gBrz)0Kh5^?T%3nIT zb1^R1Cuxc@6})}Bg1BwxZv8|QXFy#`mu|+-Prui5BkFfLIoqmSj8C*1&HhU+@X2~M z=w=mU)wDEbjyymE%X0JY6@*-S4|FiDyxUk2?oWF#M$+J$?HzO${4}Jp7LI|anNkF-@FDcs?F259%4|c!AH&RCSuW z%Ey4Ghw%(i;H)JyRj^D{6UU*a04qb_?gT}lT#8x1x3-|Cu&@XFbT5}--^c>!KTSl| z1?q|UC;Ui!Tn4At1pg6Z;IuS52R2o?M@@JH-@j*8!v5JcEg#^M>H6PHn5=^XyFHcz4!q0Q%b6}iqU zO@a7Ie*uBCNpFU$ZUJZiRYPbx>x0T%i;0$hL~3M`t$1^Zw`||)L^FB+y%2fGDlXW% zgN?sqR_m67`ZOd-0y7#l0vYnBk9s;X_HW0(JlhfJcUWDoE$SovDaZH=;T0gS+F5c2 zHfyvizD;0a!r)9a$fs{hc21Uf_Q|7|&ar)m;rcy1h=B-8Q^(IVjY0z15uS21IG-%;i$77q`VXxvr1@ zwmDDHNRWf|%DgODe^Zhi#>jYfX+fka7V}(Xv60=GUEz~@Q3kKWLWeIeM7>gduhRE; zgMs(_mn0Q}H^^L!YT$&Sc5lWuqQ$)8CS^9{LOO>-!)INowYQb-zXRfpq_iYeKP`Yg zHc3T2m|~lHt(`xa|2JKSyl1b#GMPos<9bOaXWSAP_mh~e#G>#}3OBtm`Pzo~Bl72u zCuQFEyl?SXSN&QJvTrZXPnj1s8f82kZS-3F5X`Tc0vj>rhRI-081bdpVjECIQPaz9i%i)z3FE^2sdxUc1H~N=kMLc4>bK z$$&^Ge8Yu=g6irHIC5@q))D#kR#lHP1l$od-*nDs3D7>Me1#y8Ltcp?8JKEiEvw&} z#o_SWa;-h?4|MPm{p%)+%GPW?o1GbZ914#lElgAk#&Nj%`MI+0AbOW1FYX|NImN9A|7uYG`b)?M@pcs(4#;b_r;QRksB?&+FH&+$)b8 z=9XRc-BA6Ohld>DQ1xa#jJd9PytS7WHwxf+IzfdE(yr9|>O$U4y^xsCwrq8d7wD~O zBcoNdV#hHX#*~dgC$zz>^cfQ}Ma{{Fb`ZOve2s5=V&Kup;B<68n_J1Lnjbd4-u1ap z4M)HV=Bf0(5h|J-T1UJ6AfRSSL{`-KJ-If|4!V*9Z8uuadQ{(IDSudFy0mek?1yYf z?m?f!ELm26-!>5(Y5R3os{HCsKelKe?>G7pW_n1y6TwTLg&G3a95K2Sc?;0 zdKf`0N7%=YpB6)qx>8HNgux?1c2DM^2wszaZs!~WaC3}Ld2{}eDYbTNVO+g*+)BOp zB4zKdI<>tRt7_87hpSGu1|RkZ?Dbxzb_vY2J)K>zBo zDv+q(gJG_vQA;@O07JE7wyBaWO4@od&FG+ZX${YAPQY?gh`zAjtN4Hn?f<6v{SS8d zzhP&sqfW0_{(m2S_J0_H|8FLx{hvSnf6g=ef5l7u|2CiP|GerwV}uvVf1~KIoyX27 z$wQB|LoxqHJj~Ox+r@9VLZL{+n=O$f7dt%%4}N&HCh;w#Mjo|Bkb%1z25d~0=@fxoaSQbvkyxWSZgTi)S_p;P4rpQy*8=%1<}u8;P$jVvK*YNEA6}$k z|93?~1esS^k=3i!AVK72mj8YfW(k7eC@}Sp@7(zKCt|@;LI0aRCfJfdLUEbHJivq( zHEOXH?Fs>wQ0`t`$|6Y<6F*S-S0mFR;}AF@5Inp{!iL`6kqFje4|r0Bu6-k0^|s^@ z`+CQd)E^A(*kk>O8GN@8XF7mR%I9`?>zp&LO!qZ90vU`jl9mGU?p`Z;W(wUK+s0J> z&7gO|m=d09oXINjIY!?T66EO-g31re+x4};PyoYhGzgH7hLf1}Ds*SmWWSQb06JbY z=DnvTji($$g{wC~O%6E-LT()Q4YRce%^N}@hBSsYP_{(zS@7j648&CbJ4(d&937yX z_Zzfb-#&Ks_J%SfG1Ah~_VxFFs73yRJ4ZGpiA>S_5ee2n^DQ0bDbmzfwy^vDCGbFT za&myw3lUejQIm(g?N0>oLmxpVcz%KjnmjH}#5(JCqIzAQdptDm*|rRDx~l31b7x?r zS8D-s2>Eb)p9$!W|T$v^WA8ORYi7X^;!g= zZ?0n}nE|+=nD-Q#=tK$zUO1U|(V? z44>H#^W(oAJAie~-_n8?MNLD~YGz{c6^puCB8!5;BdU*Sm;<+ZNd|ctp!RU3&K}@M zV+F^PazOST=z4K+dp8g7x_cP4O4Q3NVp2n`$h+Xe!blMzTGFb-d=1|}4valMPQ8MT zzf2Th%vKp8g?2}MISL4!2>kR5jxBh5P80OYXNI^7Q^&rX!H9jL z9C(%6_3V!{XFvZvKAwn_)UZKri$Q}SiS7;K!jGt^;@WO%b!Qqj3FPDEJt!d07~Wz) zAnH}=*#Xa8!S5e1h(|_8nRPg7bP{MZ$}|uOy>hD|y^u8%cQKT>wVNbc{1GGlVx`dm z(!gmG-a5$6;mfRj2&Tm83jvO4C`{T;ZSf1&roNBQ&KODAbb0UNW7XT&m!1d^Vo;H$ z!G1OnykRox9NKaL>PF$G9>hV}LNONNjgq?V>ww9DzDo@7#twgFg}snV=E74!P;doWmYzZZf|jEc|m9fq!%;{t@;+q^Gkv8YWz@9KwH8ay)W z6o1}7(YFw%{t|`9&aTwSO$|lgJej7SSaGcwNRQ;9Y*oE5LAt+d;093^ICO>T2@QEo<(I9wMvMkrSpAQUCx5&Aqr2Be8b-7-BF|BcTBT; zAO~)3xA7 z8lKPo?>(~Or|Cl1;veUQPP42MU>udK5(--FZZBPB+#MR;Y?a=)S)Ai=sPVm)-F~OT zR{s{GX8ZK!olDa2;|%sP7z~>erdIl(4aPDV?w>uL<1)@xEYWGxnI0NF1S3qq{#6!z z`BN?adH<^PzwbQMA%6&y4y~F8Ll~13?;6BkC0m6TD1G$wUi7OcdVFI6VYSJ%_}f-# z@=VA7u5eiY)+W??K#06cbcx>67l#&ONMbiJf~<9b)CkzOhMxciyucMUHAc=lRy z1jz2CLO788j#j-R(otTft`It6hV_wY;xp22mTx}(t+&xAR%KSW06T_zyg+O0jJ0~1 zVwvWuzbG{?r!rey{&Lt0_b{1iS~CZlW|hvv>3a;FLyJn&|7P0q=5V4`&#SS67^F_M zQ(Pq*<|8NPjNHOfz{|_ zde6Gj6*hHK963*vVJg@u%X9zI&FB6OJgmyO^^zPc){kz*zjNMbzq+y?j1<%bEe{0y z>+_0+c+0{ko7wukKe)r(&}Ukig^9)KL3!7yivYdG%QKIo!*vRUJ|#o2qt|d4Y+avz zA6RU7uwVGIbScCz6)5w5;lqpLd@~@>7D>#wFh%SIg8lL16~fPnAI=52qE2gF6eb~} zQz%Zpk*1Vg?RRMMNlZG$;)-8B2ZCF}0uh#7v}`KZq9mSrXVmC+N5*4?K1cosjHlDU zSQg-t!Jsxx)cqnbjV4^j+-cuk1uIC7B7sg16E1_MHZ%b4v87L}Z}IyxZ`_p3M|4tw zN1KzBV93CsD7aDe#pQy!dN3MDxP6xP3g~6Gyf&-`Zh-DH2QgO$>CxT_j2lAf!lY@LJ$TSt-6=vEQwJ=A3PZAviw4lV|$bBKR76> zS-e%WL1=nJUfI1u^5A*0k`VCRURtVI#$BRb(&~PY$dm;ks$T3!$DATGK7p0keTjmL z2*v8I5=5n==bt3Sash^*BIg(fZ&Ty5@#2^9Y$Z|W{b@#FUsyPFzB_zygBQ*tA>OQd zo}`{_f=6SO8O~yl;W6P$@9Gm16Mavt(d*d?0(<1;zSn=;>3n*A0P&Kx^9ydpcM#$( zGmbtfM2dDlYzuoX!)$E;M5Sa#z3qwhcZwOjty>ZsIy@6X%4VR5r--Q zmwM&F{Y#-5HoP1a>MOUTCj>_pOqBnQ?xC2!6kvi%9>>y*#F68U+w0;-y0bjwcA>WCO7&@l@rY3>Y&09Oi3a-WH1Z&G_MgO1VSAC0)D+YvL(nr#6RDo;jy6N2wCxD z@I4>}exE8<79{>w?X<&5mN=cb zH)UCASho$9<(_T{U+DF@y~DbWWM8pG<|y#-0#hChjc%iy&-bUNGJjv8>^jSF00U@l z#^Uw)TM0-!mORH7m)wTS=!lpIXG-_MQ<~xm2Dk!sP-VI zOcVB9iF+rLskAf9+BscwkN`fq0#cC+ELDj@Nx;*pU$vsGI8F+SUZ?G%gAGm778aq# z*v6gws#o2r{C{nTfnj0a>wt^V0C_wXTKJ$x|2jATa;F68=)ldGfK|T>lBKRV6pDPR zhJ3MF=^Z)^Z;o zVHqBC#)tT*bH+ujCaN(+2zFgsjziJHo)6rBKZqyeXsN|j`kCZ;g%kn{e|@x=lBqsK zWb;Wx$-$*Ln|(y}JAFh-=l5XJ+p;gBl;B+2(X029n!jWk^oeX{tFAG!)TNMN&|6x( z*=yK&Cr7Ykm3&sdD8A(ne>k=d2+Z(xGg6fj18@ya@uKBc5#Qa3bueeILbs#U<8yjo zz?3X{rrP;tqICjopk z7C`XvMHq-YjOx4Iv}`O#iW<$OjqiNK+0s>RPMyTxySp4wE$B}jP1Z8mPJi zZrB}-dEv!GzJGeow>0#^W$#}#@_-GUu+}ZW6KCcHO zw-DeNzK}kxlQ8)tx+25F&b6q7=}eliGF|^ITYa(>Cd+%f8*kzgVOFQhSy#JuVuYxU z8L>Y#mMQDK7W@#yaan|F@~#vSdvw@|gx|$(e8rz7W!R2Kqf))ZB`FfXAOOYvRL}+; z$OONP1gjI^zJvP@v}z=-tmDa_u|LLub`A(iC|mYOG#brUFc3(#Kc_a zw3u8~f9eT*bk_Zhcr}!$tO-X%A<2b;&nQ5`rNO!~hHakb)#NjIN}-A)20sx)Edt@& z1-UZ(=Z^Uc)>qpw;+V)3A>^1}_3ysm@1R9WV(uO<`+_*T*@C3`5UkJoAdaX8x~>Y; z$rE+8{U;1Fvc#m!cdAJ~>LRE9d2>sv$FrfbQGR@Cn9yNSG>RXmBm`_RAMd)+2(2Ow zr)gL+lb!aoA~1bsP6jntby;oo=Gp>c2BX#pPvcoEVN=E9I5^F3!DQEZZ&Tsl z>KaK@d(RIi{6LNXK0uJCSpV==hcDpYKk!YXqAmea9Ke@+laHQBLaXr8;q!jH1P|!5 zq>NgWsTH~;F(eV1s{bI|;pBB((X25En^4*LQ#d@7NWc8k`Mvm$1w#JCrV|BaQPyEF zZV4C6K6T!UHk;hJ07LUOPS@XQmQ|=0AYL8JO}cx?=EzzcgZb{j{zMlk=y@C&70aBa z9k0d^aryh#=EL>T@HndzAMs47Mu~P!d#Ozy8eucj!&tK!*3`H~xpqyw+FuaKi=(DI zuhj?OGi60#Mr4UF)UEy5ig&X4MFuPCxQY^g(_Fz*gF)qu+9SAt7ol1pP?)IV^6b|S zhQxw(n^B{*+WDvmd`t63-~Kk(Ql-fL!R-%@ieFJ0%NeF z^^FKqQNkhF7W8SYt#_zo%T@9uI(*#WgJG!&ck2E_w>R4D$t8i=b!}U>XV`dAEt(bS zV4ghBwwmjhRGpIuv+H6b323=`_x zuI&l}L6()32ctXV-n`+(1xLUV$tBY9HD%GoYG>?1_2eyZ^!jVZBc(cK38oBeG3&Hm zf-6df;m6uyhjp=fxt6at4Cg#XUq~FpDUIAR1&bjO6ai20Xnz4)B}ADg21>zadYwV& zOa3qWdC?>YWUj{P;RF!^UQRQEnVOk7&+bvw(-}bmxh*MvLs~Eah2kI2>v0SelsNM} z;6r%uBrDggt2O3;fiu`zZu8{+>(|;1A2*gNau7~d)VfS6i-jx2tV+3^q-EGy=8dg5 zDW#*$=6Lo)c6EEJMN)P`ebl5z=xQ1}OHlM(>h9SlEz)bEzeS2=BO@c=>-IdJ&Kq2g zX|$W?v6*fL=eLObUN?ipUL-QR3zsTVyo5AYY5J>&r_V@%joufUvhhm};~Ra@tK%n^ zMH$_=WI4Hf|L3%nJ$QhQT7N4gC*(S#Scg=Y8ToW%-h`)6M8;vgdK(Byf% zbT&wv(_DUK)Q(6+IVh6D2`;GpqDTAf23p>mF>D#-*MvfczvBu#+OpWUZ;*8Bn{Q%x zjey1bu)7_-K%>KA>wdCeAWJldVmQBaKc1?=esQ}nCA&$0N#+|diO_9bkz&TYT_d>Z z2?rvhqK+1CVi05Y_lSl^2j6o1y8wKW;aXq_4g{x<%~(+e+1jEzR>Aj^RHTA_CKI`N z0O|_bj_H-awFC5#&U;*Dd7chcGX2_S0&2HKKyIR@MS zjbsDa9mjyH-;FXpGkQth3JP58Fl{BoxCk0rNdD&w{fZf82_-%=rOKXGMl})$G7-WA z>S}+~cE&QGCVi##b=D{7x4UI=IWkf*E>~6Mu%MO?Y4CD2f}pp)C=mroicO^oR&@d0 zoOwH=;v%~9cLtg@vQ@g_cRk|h&|vp3Hu z`rZg3xBxo=VjMT>!G*+>}qXqp8GFoS`af_L%wN zyxGa3Q)~9}?3l!qRPA^Y{1rIc3D_9`i*^CE5aUpo3#3-7*>;e~UeRfFj?@&;(kMTB z{H@>9D1D6WxInMU-`zM(TUgdh$XM}3h@X%^=Dsw<*^|RZ_<@RiZ7A}_I+9LG%IA$b zy!9VnRus$+$iWcV56TX!%V{UEuPM~`;8Snb&%}>Ad@U_@fXo1-V96PK0t~U&hy(9r zzRi~Fm<~;UrO5k*KW>!G=PYMvxDxd2pTT9XucFNsxRMVpeOoghRN!Y7(=btt06P$O z8n^3DX!r#L%AX%{a2cQVYt3}B1R9owm&|4R39#d4EAT}<4tJ^LlS^hw=M3NOoSd*) z-F|*R54>5v%+2j;S|xppgX2ZH_wSG&`h2|@0=&OAHIux8fS=x=YtF>;bP|Y-K_bB6 zcl);Lat{W6dt5Iy)eHPmf{z618y63u&m4|t8|J@${Q{)nM9CrN4_|#+-6$?H9BIDh z^EoW-f_2)k)y;b>(@3jan-Eu{L@``5F{G}})x-qwZ{v&tu1Y?C4jA2UIGA(PueY+E zDPck#FMp|FyW5X{><4ltlSX&7X9!|%R$LO%ovjE8&ONnv3}`wb@JBZ zWVt}I>@78y-_4VAdpeS!3M>Fg<;%@C|C|(u33d(0@;&?0=k1E$d9{+!t3*0;5)5nH z#o)H_yTqXFM!(aKkOF7zWc8g{7n^ z;M=p|AT}+Fi4qdX#eI7vJF5gDCey#c2zwh-);Aj8y7yOK?(CmK$O8W_lCC-|s_pF# z4H6C^-6h@9-3&;Fv>@HxrJ#V6AR%2MB_Q1;4bt7+-3{Nq-|xTs+y`gooU_l~@4MD| z*SZm&Kf28^iyNTk&z6*ZdXF`GIQFBqI}{Pc|7N%G zm0t)^3m8a)*Bs!6JX3~3(%^eyJ%C><4W)Ttp{i{Q*^_+x15ofc-O)pZ|(t;D&x(55{PSN*Q;{ti`!&l8=|6;02P?ya!W~Ngha)E z0rfJdQ50hIzhv zNxEr5?09{_(sgVV(cUkB3o^DGR#6dK&Xt?~D0PQ`Bu+(sL=LQ4^Hnkdmtpjcwy&$p zd!k^~D1aPtvRC{VL#7CO`?lF>H77Ji=oMr4kKRtP-ZHJ?EyW}@ox%waYCR=bRH)=_ zUH=eLgrK zZ8nOQPZ7z@30?Yny;F1mH`#)B7k`2NXJdi8I#o(Iq9?_Y;N7 zi%$BS-4*f`m1X?=JGeM|_cuNWZDH7sH+OeF-rh`T{UN=Oi;~)<$jBEcK|6a|;4lX7 zVi`E=wWABG0v@{ulGp=J+8f#39Lg3X_;jY*gG0-1Ee$)mFJ;v1#(t`a2L*yJgHX~^ z%j0#e_uy)%55XfvV&?%3>i0e#|2q{>D^u|7RA-dAArQHgZj=}{Qh}ec9<%J~G=(58 z@3jnc)nZRK`@OLrqEg;KARsWI{6k|l4GN>aweR%-P`<{-u4>uJB8ZbLC&rHrBypRo znW9s!A7LZ5v&}L3H&KZb(#*n?EGk1&=bMxH;cJKv1Iu&~f+Rl$_e7C&d@$nbBLoN# zRlFVp z==d$XPU4xO?B7$dD6FhUdxfIBykmid?Ny5*YMs&su&XTY^0|&(i&yWYs_biU8Zr$G zY~>}>TXc~-oJx|UR8R*>MhM5gnDB1AzKc*llcdvd}TUcA1@x8nQ#3U**izL;oxEWU5xqyMG`2S zloS-+DkunebKQbF-V;rBztZ6;@DIE@qwVdSjJutmBhlj+&}^uLgi^O}CnhR8m2-== zqDzego?)xm{&L_K%IJa~YWTc~Hr#yf3IINO&D|C_NUt*E;`-11?CgLy%b?jdKO1FR z;9QkL94!KJ2r!{&YZKSeA?TyVg;ncCWo8~1YUBf1xqGX5G85yAc=g=Tf+^GrY1>Y% z5^S9}uep!gGCF|Ee2!JC+b&nnDQI&@wPK((^M_#XUtgt>6Y{Wo-rM&#SECPkLEK_7 zxF?52k<4MDK3`KpL;Y7uDmWA)5Hm~jS6P)$dO9K!3I>@#!Ou3YjkZ&8Iw|0*q{Hw- z#>%8nXAe!Stitm1^Q~f`l3@FBa3}?x6R?#8|2;5peRcH$6O)P>TSj38ge$xQo{nlw zZ0OR7<`Ny#zjbLiId=!xUsqX=GXa%13yaxIfkZzRLyt+LAqF}y8^MhIT|uj0gQ5j` zDNqKc$2zOwE7w(i z?^)q^Ua+hp3M}p?xL*{)UIiNY1b3}d^A7l0daGas+})@xi4*!Nl9`KG@R0|3SET$r4N#@6-_x09=aUMOWzmCe)cq% z#}2c)E;aN#SkO{^=XG*o(#o+cSFP~7Balt^aeu*^-3>e;+sCHGX0&gSibUMMhEsSC zeT^M6x9n5n4>=eG#7RRx%8A1U7?wD8VAW2_ZFzl9XC;?PjM=C(ECvJ_r3K9I$rsNm zQNOjFllCyqneYgrLyz0{4@X`MU{lwV~4-a8Vcsh39Bt*(? z+9T=;7a_Nz3wwBE2(=oW<+{goY*~>O>Zg-TOw}({ z4~C7Ka;2El_QblpQX}aWz3E3>kP~6!r=OCXQ=Fmkp(9ZQa9L;eD9KRuxgW*WB-2|Q zD4yJbgeot9ydrhdrZ!wJJtb~CubVo-DrqPki%ir7B=sI!{_E`8D9*9D4shP=c1ny4_e+{93Bt{**+AZblT!5norM92@tfA`m9X(IR2K*V9UTvP0Q zP7iEta{NINXWpvx&$+ae4Mtd4-utj$b`Dr@)9M+ok3Bzf<4>AYMduT@y~X3#Ip`*= zc&w|5!CQwIw$2~PN!2+yn8!1k44;>!UCbd1LDhgD>Lelg1g~pe&G&#Ul2Pug3=fPL zI0BRe?=UB84f}7%w|UJ8CRR+3wNA#5+w2B)Kfb5$AFaxUDbdbVTIw_|wt%DfxFxkz zy--+01Q&<+;^^^f?stS(R7rWY^Ge;r2z*wc^(Yy&!b4oOS-$0xp@9|3=5`Y@M|jMp z1cN-_K8IQ%LBS$TCPcvez?}nd7s$*D7WhE{p10!QQ;pW!XP|78@BqWF- zk11hgcb9QstXdCKxa>ivX8cds2HcQKjR_+vW`hiCP>LyswtE;}G3Dtfj-RV)s^;JzmQ*6sX04^0`tnD+y17m4y7BeO zYrq7^kg;Jeq97|f8*SDc^-mvt&Jos;Z&8&a!lLF_c%x)p4^t3fXHoI$!4Wh)%U*-} zdx610(z#~5;GL-MR>0(f-bb~(c>NywEpG%^Y6X8M_tJdoH|Og$qMo(WmJ3E$A(2UU z%vb2L=6KIRpa*dI_La_Ej_RfU>k*T0*+1I~)Qcyh;lqHLb3A-E>V!GBbgm23fm5oA z5c=ZFcWu@D+KzgurX$SW;C}Y zgX0wN9+g}FbX%Q2Sjqp%z%kDyAJ>?2_Gsc8|f2lFh8+A zRkp-$J2eb2Pci|UANir7--}J|6Ab~O-MZ@RIzaZK8dfq|aDINyieEe=!K}blw)z4c zJt9I8+@S$qAyg{q^2*fl)DNN82csfy-dt`JZh)@q@oc~V)<;5vtOmn||K9iWBCHn? zs-vlCS2@c=9Hm!0WyO=j5KS(U15+}WnRxZ#_oR)Pu7|tlQnTyw#jY4K5>ll9{vaeU zFt~F4G*{vIewh!}ms{M!e>PK2qoFZ|&!)#^yEVMNOfJPZxqA^u3)Vismt++f5ntFu zeO>q3Jo?~gc#0STIcLbW4!*nGr^^)d=Vi4&UV3cwJg)aT9qSA?MnXpES9%%md~Lgh zKnh)MMaaTC(keW*yh#RZG!i(Dnt>Le`Awb(%iV?oE;yQpV;0s>o}6d(6J48oD=u0>r=Eylce zp=^1E8K>j_fSH#<+|TKKTw>mX>9VBYT!aLMt#1td$4&P}W@cuU7H?Y~JzT&jHNxHX z{6$hPCB}`SVT=P{T&f3a~(?(ml!m=_e3)7et zPql3iCh<*CMC17&ZyFBbbH_w-Dz78Gw5ZF5eAy9#!J@&_b=Njlv=6n$Z<*97d&56Y z&JGT2@EeDRlO_7wi6yDHu}hl}7r`dG_OD_(CB(cJQOIcS<|M0CUc*<9=L9TGevG(O z8*6)KdqDx1sZ)*VFU1+VY|L0VAGz*p4~0DSO0=tCT5sqrR@&g;r~*1+aBx#uyBdLl zfvbN}j$6(bFDS4``9lN_j*q8nJ`c#Rk(7Qc~z~XZzk; zEj8^9lwFJzOlmf$-6uh*cW&a~*X1((eofl+#E`82bf%_Wt-6^w9d8{<_oS!$3|(7a zU##@4{l!XxOM(RXEvduhx*V|GoFURcl!h^$|Li5eeO{>|s%&MHIoD=a&hn$#YkZ5< zBNfxc0A!A=c6_Px;>RU9hhxHLO3>5&klC&NzJIuVagcStKcNWf?>Nejug6Hg7BMWA zEadsLgm4c`%w5+u6teKBerntLH8nN39~VvUw4`vXuCzgs&KA`20SUMhQ;b-?o5 zu+gOZBALB)!GA$0*ZYl5QODb?f&wVj$gUexdOzsx8Fxb_ZBDO>fBW&?s}Fs_O#R!@ zk(QROoW`Fwz3+1!kY{md(+?uO4C)v8sIfq;4Qfum2QSm{#Hb14S58j1l2dXN{(DD9 zSFL4Y?2BG4avI`!I8fwoRIG<-;5dDL@~18eN#K2R-_QDn6$-DLh>0xK@#Rv#%EsgF zBH4X^N={bR&G$AhXJHMK`Dt?3{m|HIeRqE}%eh(b)Hzo!Ml3*h)S{+%LI+(Km*07f zw!6UhnhjVI*Uep%N^U$WDSxM(ot^BPpBH*_c>q9a{TllscTI%0QbXUj^p&BZa<`Kt zu62L1oF4B2)6+2Qg=)^OuI*-i29(UqaDM2f5O6W*gHtWkR8V+5KAiqC994bo6JXux z6o7-^rY1iFgHunmA)thfmNNR^*wZ^7&H-=eQuBF>!a_@X-g%(Kvzq;te6}|=D0JYV zM#N{H1qeAzVjiop-_B>d;{fo8iVWKstJDZ;&8(~(-yBG0O6Xsl*Sb9@?BoXhT1-TQ zy9@V%SNzA(B|{YTp`W}CX;8ylm|GMRXwsjb<|zPp!cDhnr}}P%mByVYGPQ50-^0^W z<4TZ1166ev$C%j5Dds3txl9M`)1m`26O+rON4+@`=Rc}G>V$Ygf&b2aFSoo2L=ggF zoC=4IFRuok55_8S%T^%}+HZ%Q13kF>Ssrv7XwnJ~2j9zhL0VU~eNu9W&(q4ju+Sdz!ln}gM#syPK%1WFv#TqZaDdD0+4A}MS?j}c{>`rxMpP;$sf2=l-bVX@sglyt(#yT+ z$pRkXVGVeBBrzqB%w6UR;OMCaEp)ZG8prw5Clm?|_!2=Lf~Yh1OFJ-*LLGAAOb3hvQ*FJ^QF+yQLVd|JGts;`n3myZ+s4J%0KvA9QnAyicRS@ z%r>J51?XSD-tHn0^78RE>KoFnwsAj8=H?E#8S0wUtr;M6PGU0@1&n5XW*{1*?BYo6 zoADou!3IwdloxuOuU)98P=nJ+)kqugbURJ*{8+0skT_Y?1L&>cj9b@|x_9ZyiVigJ zFx1tFn}=C+$7t_}?b}jAp~m_~dUC}(i?yHQeU?l2@0-^4!C)hh!RuGJ&1QGVt?zew z-+R$z3UfQQf0~$>C~LiyWGw(~S1@gOX5d{b@P2T&n&uVeV%c|@G7c;g&4}l5@0WgU z8Elv2O;&rjOm^IIZQ|wiZX2jaZM4+a`iR5^`-j=RY#&l7M@hKPdHfX0PX|XGH#fIc zGA^Pji-ESrTg21pIM#W6`Sk3o+Th{M$kYHca(G&!8^9tL=Ks)r9q=T2K$;~v ziJ)OBxie1mq{2vvMM_F~=Hhz%ntECC5#Aqohacj;Rwfh6a1-Djx2YE77Ysp0>6!Yy zO0bujX$aM?+ydFz&v(9WQ~~k{?Iuk)R^%XtgBBRK9vKHdH3vM(h5+-NsPy!-DB1C; zl6la0g3bUeb{5Hz2KNB~EBjq9MrL;)quktES^}4a$AP8hd$Ie2^_iQ!QZ_dH2={?R z`_dyeY3ltcdtfa22Y50$P)dfaJLxV6CuDn{TLeBo4)0Aqd-?iKmKjf5aSh(vuL7tI z8Pf;zR6fx>(r0oEU^L@?9iC|lT|6Po0au?*KMsLJR-yDGm5~61dpRMtJ>E*%9PTg z9xQzTFZK3DBQ_FnrwV}}dwTLI!~r6Z42QV${BKQ(P>sXluj%~(6voB0(Fo4h16*ZW zDSVC|XB&kAt^tf}Y%fq>@Yfx$B$mt@7^Io&vhB0w;W2C?1$2E)AYeC&Gat*6FbtOm z0!ZL-)zd=)G1J}RAF`=dO5v$?Y+X8XC>8QNnEhk&y|K}^;|pexB#2%GxB6s&y;Xpp zU$a(9nXjEs^Wxc zC*4b9iIwr;Jn$#GJLT)ZogWn7eRZG;%u|Yz-QV2o>s%)EY`kV4%~@Luf;_*bLGTHj zlW70=(Wkkkpl$>Fn`|w~0wo74q*%24j%^SzVo*m}pozV@8smhBc5!$Q8wHqDTup|@r~_-*LC`7#?w*1uYo~&5{s?}8=Z(SQ~X>IJimYdn0dLL+Tjy&b3lhs zA|$kH+GTKE`chDofsnL%?}WsmLIZ)b)bQ`bZ&%T#pg?*OHIuT9ne`QhCgzTppoX5MPYV8*Bad6J&zCA(2OM*ClM3jJy%;&NC zqtutyUpL0-Ld`dOw!u$&m!1PtgU3)F6-FfZ9~}`}Y>y6iFDY7{9eUUf!#XxT}Z>bVK1xcn}u_7QKTpf$OOEX^ESVM%s z51$LSdM-bKtWZG)VbxnZH_eIZWu=GhV|PQNqo4A$CHMa8bC~;Lk2@V>Lg*uPYU?I6 zvTO!_%w~4l-=k1E-MPWmWzmkC&uemh3-yaf3#LowM{0H0Rg7OUCD8fdhNwC__oe18 z0h4(k_&YgqPI?=nVG=Pa=zXRMaOB-(6?Ydth7(Hg`apE^z#!2xOSy91xg zMaYwljA5ZTk@&B?08d1z8eM}$f_kiwrk;!z7q*UPlIIf9^=6VW=zx4C_a-4=tD^eR zbM1^3#7XZ#{7S>S^CY{{^0~>hiz4u;9#*0~YnMZ-_!JNjjEouiwUBmpL%T%Zpe3!| zZTHwQCUE#Y2Ohzz;)x=@WzwY@LuRH?2hd$+h}_>FGxIvmnO4q&xlrKC*J#$qrB=nf zz0}o3$D3N>cHGAnDvcOL#C>*uY^jub)d8B{T0Q$XT2TMx8MiL=LqNOGxM;~lvWL6bmqgr5l>128G~95PrzR|6%&sZD)jylhYa?LV)Q=G8bTA_@kEl1$^NjJ$u-)A`AxX zRW4sG|C7yCbCx&qEBDvdo=SOR`}%E4zjIS)=zS2JKLM+rDOYkSnjj^uLPc(6$IYqZ zf5al+vfuvtI_uZ51ZDxybZa4Fey}Tlr(b2I9474X&nrq6_`(1c*4_27eu-?ARYFmx zCh;4an;343fYYvL&~$?wGsCLuz00$2e8@Pl^gIrA&u#6X69gw=2&apytL*~6N&6=m z5JAs4WK%iIg3sJdD$i5ov)LYO{JdA}NU!vR-x=gsT^*+iV4!!gGV84>TxK(@@arjE6no^6!;?{AA5M8w$iyjK}C z(FD0tmDCcLJ$Duo-xXAJ07-_N2dPkM;??bIibn7H8Aa(3=xe9NMi0yv7-!e#xgKBn zLpXW$S;*$9OVpd=-ce)2G?B9<9`6Pc%XEo5}T@CU0&~C+Us*S1iy5G85f*AJkR7vYMqOQTbm@*Pll)YnMKF zo^K^JwDh72zcRU;3tR&tkIE-F${L4D-V%NL<@)pLY2<=QCYw-+VB_Yi)LaPaUT2Tv z%#CA!$w$i|6#2Z5O?pvNHWg76*BJ~7V3*t4T4hQ_r4(m!^D|bTt9M;~n|`q2pjz67 zw(y7Vi`{a$>v4-eJ5noz`hBEgWH*73$8X*I2CH~%c<_AQN3VNDvokPIQn$k+!()${ za9gM#U>9J{XoQ|Rp`Bc-C<&VIePyb4Dl@t3Pc=tpEYr#(b#*1B=iOUBzHCQ1{_o|9 zQ^|bVKc2*UKXXDw8gnd#i2#?wWj|T!4Pen0f0Ut2F-P81cR@o{)%WQ)UagcAee}%x zleT4-Ww;c98#|*pIn;c7)5Md-a$TDATnREbMgkY-!(y{F_HqFMEkL^vkvPcB%?*wl zka-U?CkCe}m~+%A(hP^?^&7^?I4lTLv9Q=Jw;&`Zu@_J5?d%i?y4$#t%E=I8lZ&qI zvl|*3#{UsV4C}439CiR92+>9A^?oL#FvcENnlCRgv9JW)_o_leQLq)1q6*4d?)*Sg z+9lHk>XgA$K1D^v2oRE9to6O0N3f_LXwX32sTR9;c~|~nDc)>$a54c3v#-(`z+YNg zl97>#dt0Q_B($eZti zp*iC=p#IMFIGigiWF(HN0g6V@B=u5Dpepj#zr9aPVAh#yJr>vYsL9R!psBf8SEon# zK{sx3!C=YP69D!(tU-M0O&3Z2_eC||^ zM`Y~GjUZqLtjE^Y#x1fpZnzU^4gNgP?D+4F<^BDegX5~8wPUO3 zSG$S^DoNS{rz1*$R>AE8e8Rtf|E{kI=FwO{vGWs_7Z?6-*}_y6lCd3_F60Ty3#^^x zvc-rJncU8EzuAms#dITnubmOw;7}fiG*#vri-dly2L!)_!|(d+Hh4iOf7-!>1*o-q zhuJw&B4>4H1Zmd_WdAN!*n)zVYOap1{NlChsZlYloUnHjRnFp0A! zt&Ro4;Nf!QD|$g+ogQqYie(|8wh${(kx1Mauc}M0^&z*YTQ;Etqe4qt(v>#iV1NJ7 zCDS3VY=%mhny1Ny0s76pb9gx4^4LLKq6WJwO$Jo~zfY`fTI{CFc36&?Dh>m+zM+AZ zr?{+%qI3^VGd%qFCu#{qU3zo6Z`hR7I668>0SLYbNLk|2)Vdp4D?wR*GIU6!a!r}! zw2CpfdOg-wSC@Xuz(cEA)C4T0duTl5Vt=PSl2Pj|zb1SvPK zw@NAi>o%Q{-oLbduy)~+c?5nJ&NHO-h{_PwB?SkUNBVbApHEAf-aMHhCUnnPt5{0Q z{GWO3gf%q(w_f}R*LPI7xZf3lL5{U6N&S3%4!BxPK@fhdfr?1)i3p#20rqN ze1xQiiv~x&5f9En5W)|pd4qa9OF%f+fJqWVE+lGkal>Y4T%`S`dv?XHTu(z?L+A1G z`lD1RAWb|$g;ejl+jpD`ERvCpy3~ZAB+eW=MLk+28MCEv?T!&tI_B-5*PhkEHSL!1?oN|;3x!C1kXW#DjaHDS>2`Q#e`jB1u z`^cTQoENAGTV9N{gASF2lQs#k&yWOG_Yl=W={0_={zW~_yI5Pnqn)QciW958hfUY* z!Tb;(uZI09!E|lg>RBzD%XG)gL5ukoSDm21w(dZ_YKKKGj!xqm^~hKkOJFQ5WZ(Z& z3&2u9bO5;CZH`j?+M(23S+85*Ta*Kz7bfQ|aJ#IOR^_=I0QPSk0)i6l2nXWw)OF*s zMm%_hMBw+JSCW0Bw@Ha8pQpV{#qv=IS82bc9)zu1$o)5*$`$i1`zI8n#Ku%(Kp z!V(9oct|t1-`KpJ?bXPN|D>F+6AuTs&&rD~$?&{IM2)KT!4#tQ?c?~$W8+I;+;#|* zz~mX^(SR>WXKT%ZP$>;Wh0EqD2zqldaHy7Ov%hQdI;c|%YfRjAEZ4*S8y>DeI*hv+ z0pSP*zG_X)O&`U*n;9cH7%Q6?oZ4c@?k#|j4TwAdes`OexebnymNS?5<-5-rc$sC3z0h9q|vCz~=C|hQD z1T^u0D=$xI0S`vU1t*?}ij4-Z`Schkqh0FvVH?^Tr*wd45(0s1m?O|D<;2Opu=x-l zK@)TAAZhgY>beK_WT2`KqCd50^07IHk^tGAW3-o%C)QOaIqyab07EAgjc9 z=_K_!Ook1a{o- z>7x~`aPrL%IP7F7H)C@>DwTMrG!$N@?LryhGn7t~XJDsjXXUFVX4YM)Z7@w{I^`PXS}o5<#`piV%eTmvErMZP)?jDz6)Inrat*B_;WF@IDn8!0MkHu1V{rI1DI-dL~|L7fl{`?s* zaG%-+sNu>rOMVA@Z-}bpS!nrSDIpO%rLdpn`6t5E!0w>OxJiyBXkg=q00d(Ki!yl7 zA@yYNCqS7141gJfx{f`(U+*u36-_L%<-xgQzGemD0nNA)@si^6ikUc#m`Wfxy;q@? zRI1wl(#BW8_%sL(Gz6p1C@+b2?3}jBgQ`#Z?v3fw`JF?PL!cm?=k!C*9n#Ge#mQWf zaR7h{q@(nJ8rC#2NW8l4hmA954UTVjUZlOg=g^~l#-z=o$JjH+Up_)O7*^ZwS+V1p z`u%YgQHO7;I-j+gG6sg34yM*O8{Y;>Ll6-qoIG5Z*w|!onDo;@2B-pB{fAjDnXbQL z6P_D0*%hD7{y!dLXWMuy;H+<(JMgZ3a8S#ea`xbAEzVT3jLw`|-22vBuS7O?^b-bn zk8&8gr+%OIB~io=Lg`*rxP4J&=x2Vq+Yc9P4!HVF1T;{j_+DgB+rya8z~;DuQx0|3 z{wk}jjUphe!L0p^!gBBh8rJT@IZ~hI96HcGD8&6&{8?>6>C$s2i0~k&P?9bix5f-= zNVW20o@G9pq0pP(*(IlIDii}Q|A0GJQ1b07?P;!5?h=9bnY(n>#R8QyUdYbULO%4m9xJ`tu6$~!m>Q4 zWI{fJ<*3f1@D4LhngLE#N-dzT%QeYd0r`!Y<))cI(~W6QH0PUKL<2q6eLKCm^!eXj zE+As1`^S8vW2WiHzKX~<1zA};J;xRlVsRmpI}$^PyISEpW5IS12q6D{>g5uRl$GVj zpyUvuLvX+cdZn6jP9wiXi^7aXeOOua#G(*5Tb!WyquP)_{Z)M`&(8HDk~GwdN?6!# zp+0sJg5-lT8Mt0cox_a72_H(Ve8%Rro zsMOb$l`em<6ykn&Mjp+#F0+00tgvM?=o@0~XG6w@QW{e%D=!MR2)KCNeFhs&eiqk< zl)u1yCxMkyH&K9$t)EpvN$CX^)|7y?8ObP76#$N3D`!-Z$oxjurH+TFOQ}ixcaP}Q z;NjTPhU?N2W^?tYPnb+dPx|x5$A%KcE@V(=WjGl)+WNXqfB;XA*HP5Ee*)|WXFkdT zFJg++iKzJfHh#DWnT118f>uoLHa|5{yXDLA;Jr#PsE?N_5jVHtAl%~(d)_-AUNA89 zJX{)_TKKvJc~`<~9_9aa9gbn*w>B%yx@mw19L`9tD$ie1dhNaS{mWa+OdhLuI%@VU z!!{6|cAe)1I6q_d#Z{W?)56C3A*uR<#o7}S|HWqB@QxpIRMcDtfy;hVr=LAEzu3l{ z_qd=?Y^=w)g1APMpd%=}G%DOH+Si%*GK|z*q@DLFvCV-f;!wnDJuOon_=asCI#eoZ z4W@R*MdO>((BofzLe3LmtHCJEqAoKd!@y&AD`xz@c}X7{VpMirBovT(nxx2fqcovmL%$#aSPJTq6Ykhqr0-@x0%3)AW7Zw3@xpF?UMsnTl z8X;BmI<0OFj{RuM?8>yUam~Z^GQo4ZHK*izT>B5*%_2vKo-ZQ4{=tWP`MF*uTtR_t zNI4ogl;l%TfjegKvO zZ;`Y9Jks{qq`{vsrOmnOHH*Ov*8_3t{H`>7ps}V1_%YkK9E85jPMPhtT4$1iE%}WY zNJ?fNH;r1$-i1$~+F}Fu4mYr$P*PF`B`>^El0<<;%2w;qq5jp_1C~66WS=AvKnr|D z!H+5|Os$|n_b+8zfO__>BSH4j_ze&ko|Cyvc;?mAwI-JayCYz-O{60W>N&C66&5lPjqRes27M<~=x zsU7*_+-xF+Je($CIejEzl1=rwOD11A0}Bm}oaaUh-~=>m&y-j!*~ z2mbSo@b3vsTK$6qZxVmpf${NO0^@gO6jdahFB-3RTmZO7B^BD!)nzjQ6tLq%zw&?& zwVv0e>L@iDRvzO}8Q_lsS94?(lkU9kfByQJFf)`9EpZgw^mQ4-r8&)igr9vJge zrJvptVdO?Mp-rLFVNZ$?yL;bD($O95?+ZrK0Mq=VV||JI@u-mklZ>~*rkW6?S<1)f zZu8%7iLz!rk(weqx-?{5Nf49PzkkFiQ?}1npbvx>j;~b~o7{_QYdyg%+bR$Utl799 z-r<^@Gi6*HwtubbJq>L#=1U5$y_k@dp(uYnfsb?q@XC_c-}{xPz@F?AD#}4#feo% zE8Rk-nx(%-^wug-cLy{s#ObD2t&gc|G6v}rIZT;DGOT}Mc5tLfS5bw%EG{mNS;qxP zXS9ysL3|TFBcRvl5#q^ZZoBB5vI6{LA1KHsVyjl8WmNBO-`z<`p&BS z-G`ir0Wj=|vdI?cT_zuR3og%$XF_fq1K}EvUjs!F=RXC#htO_6Ahqa&8M;OLKh@rRcDCkmRmBtXMK z$d@Y@|Lx}+93gM%@yJ0VoaYg=eB_=bv$O+*iMj5cZSs9- zRz5fhOElI^^cG3-y4gUX2ms>N0}o%&!Xr0Lh04ME$mh_-tNx==P1sU}hfGn5LpIRr5teoNfC z@{AE?y}Og(DrUByUZIGVown5w(m0kFe;E%ymT3D^X+3J49YG$lw~um|h$|iy z)Y#R_Jbs)hHXZo1sU!Bt(d^?lxi4x}&-vjw!!nGE*$aYQ;I>aBiPj~UhwrurE|0$Pe7zQuESg+>(m$n{9uoP&!nRD_S1|@@i)8Sqhc5mXCQBq0~ zYX6*{Jt9k<)K830cDbIs#vHO035l2~E`|F(UrQYc4M_S(o=&?@Yc(V#xcVwCmDh9Q z$(l@76OFbHF1GLpMy%B?S-gXg6tu->u$T|th;g=tOKi_*oppulqV56h`n#9d55p;IhNAdY36qn=@BphfL8r-Hn z`|lnd%65g;M`&%t{z^>z2) zv)B3!bJE@LY5h~cB1f0LsbkW>MllSacL(t`|3_ zZ0Uk<-l(Z7*T)c5YyioVVSbsYv|Pn54s|7cooaoV!YMZRgPfFHy5wXBULjeelR@mX z0F#*4?$1hX{pza81hNui=4o&HH=nEDx$P@kyLV2Wf*J9oU|*;cEdTfs4lpiK2PmEI zIS8F%vY&-35@+z)jK*lG+W96zA9U=xaxvkkLnz^7WJ5CfonMeQD)qZ1>jX{2%;2E|cq& z_V*k|Lm1ie;)+QRM_Ha$)8&}uv7x5O>nMJzz;Tn6o0*xNnF2jVxUN!(J9H2kS$Ha! zkJ1Cbrp-&~PRv{PHd$8}NDvUtB3V39sb-ZInhbp}56W!@_bU6gy+r8$p@55te4f%s z6kuH{x4u5@?1X!~f5Ak)5sF*3qu!Pe%+C+S zfvuuI{8SM|8A+~QwTnPcSDDU9zXZ)*m}6@c)X)bkK`Oxh*JY zLc$WNjz)4|3H`1X&!#L~&NlyB0A#-d)10r?9*`pDYHc;DwJ-dOCB^(uAXJFdozv0W z$&ukb-@g&h*bIRSy-wE3`_j3tK>wYy#e+0#c&;8-5|x44V=VCfaZpf@wMGC3ekwm} z1d=JZDf+L}M%T{^iI!EK%iW6;S?AvP*G)Z3IYGkY$ri~#j>P5huR-abFZbVrlxU6& zW{_9@6AX45fTtqoi9#(Jd`SeA6o<-#Uf$o|zq$2pFE!GYKtLqXJozmI+!Y7}R;Vs{ zdeV}s@yl;IN4Ii{OdpEwy+a*x)4rZth&Y(S+cz*Q=5j(fMd^Xs_TQo_Ta71n+gK@Y zdU&vSn;M_+Ie^|gngNb!91h`LRz~Y~HB|R?Ovc(}n)+JGAR;*7n#9ogJ9kf}Fom{; zjTg$L8nww%2k<|UfBeMtUx6p=lunT2Iwh~|4P>^LYx_>Z`4c3jd~x7G)1k3h0EE0cz8n+qu_G$efuUERluD z1{H?d9Q-6orTj3uW8#>^K^ndIkK9 zR0vpHuYZZ<#cO={DX9>uGL&}9O=Y(8MTA+%bc3C1{V%B~l!p1q; zT9=gh-_kMEeYWn~9nkrGFkM@lUnW#O9$K-cV}=5OK-H^$7KUc(`<2wWZjU`1UB=XR zu2F21IcqXo4#acu1%a;}@DPK%fA!ZU{(C~BS3AHRY9@+x@{N~`qKB|5HnH`dAwglE zMXN&0$)0R)_kfpW>+{mQO-TyufA5?Su&j%*!=Fzwej8T&>fW>#>!oumiZ!`9P02}b z7aJmma2ia>=yIAkcT}@gViAnD2-cGX!Out}GD2if%TZngw|gP4`7z|7LbtFBAPi=P z{Yy>W^QRvaKEgeb8QW1n*W~QQH|5xh&qqihFgOmnct5^Nlyqf2vmIOMoHm31)?nRY zN;t#Ts0#NMn8%29#Ogpr@V2tu8efCNi{O>xe?;`XCZB1twsm$^01W)iP2I@}htU8W zq!+@a>S8}1`QJj>9Tfximl$nHd&fT_{^fty@HkxZJzb-7WG@5GY|_%7;gDuwrytZN z{H(CVb;<5?{>A~U4KSWZqxsC`P{bdb2;5*Jk4kP8|AWt-{_JZy7Vf;>%`YG%YOp(; z-wPMSb&DPui(HUSE|gg1xcqd!&2CjaH!`wv^5>)t66opaDbW@Db&wpa_luI-_bA1a ziOC7>r)yo@g@pt|#*|zonv4|z!427~?Li;k?XD{Y`uxBW-!&=4H;f%l&$a*wKw^6yIFhL#uw=5xe8B`^)k$*}&Kh!@h2lBlnf6&V- z!j6<V({V!=#KX>NzSR1oB71LR;I&A>b;38B7y{5p~iYwf7&zK|QdGdUJ7c;pFUb zb#>FIjz#A-NuAZ~W|Q+k-Z1;J?$MIM$6{2S(+%SDWLk@3t_7T?fI2heu%kf z+vlnXSBu9zFeFnJ=$QUpn-}-j2H3M!>a~2POk86(YwN-6K&2504<*OM^Jt&27y?!s zE8RKxQiO~%yOkHCadI1w_)P@7?WBi?b4O6)``PUyG6#W6y{u>Z(G6+k-5*S3*0okWF*%ua1pdJ#zI*j^;{Sr8Nm> zg1*Ta(S8PG5cn)FQ>>u4Cnd(ss<4^fUxR=PTs+#@nfYz~eP(8_DH?^SpkPCaGLK)l zk@>#|$8!f4WEz&5A>EEXC*ev}<)BZ3kNiqBj#TD{jfcz;f8H7RP zMLub$I3}Gpojyo&4qVBCH){{RqYz_Xfnx|ljgP07Gk%-+{yMDvz2<~^i%tRq zXE!PWDg;&h(?7-8&}dRomzy7p4l!k9(;y%sHc{+OCAbMuMqQgKzBaR zT5HZZ#y$FGm3yTa2CONsQC74Z3cN%in5%8ygMeK}E>(`cheZVclg=WDs1saR)JaMvAtvFl ze9_HzVutLiSQ>6JYgtbCAsFfVcR+A=bp@}|HQFmE|3j7x78~2BxA$WoHf@kR&3G<6 zA4EQA^n(0LW|qbB?w3c3!sEky5`XcdVvQ~3@#RyUSnoM+coQ3lFE2!%NgCZKSOtPF z@}sRGV_={F^%Z+rSvims0mIRt=}U>yPvYQG&{u)7cKML!Oj;# z@?w$~0=dXR)`oRzbcA9$*owTAFeNY#M4*_@%N!bCXu$=)3q=X`dL@ztAAGdHqTkGqEidH`Fd>B;&C-`!Yc)63|kpe5B z1$JBy3p;<`Qd4&UB*$80WZ;w@MBI3Gv3+s0b#!sD@XN;gRETYwi1pRZB`f{_oJ)Kq zX{nc2{q6g3k*b%{k;YrcLu~y6IWm>VSVR=;p>fWb{pQ+*p8#!$nHf~&m|CR^ zBoNJ5VDv8J+UpDg^C8QTG|&90LEe!vrqOZDKaC^V zy5tr0p8?-P8+-W9xaS(L%}h^gho?4*7iQbeaa9Qa^nJP0z_#L&cZ$3Yj6NNQF1sDY znM||71*qU95r5qFPjwfLcj^mh&NzTu01a8dhwdvY!D?=3u_Sb#t3<V-7*a7T&ssDSktfV7d@cFn@H=5f)lV7wYh(HJ0;Hk-% zd1#2!N|hGxG#aTdg27ni39uU)bEvp#WqMl#XSqXA@C~ zQ_tm@1no1bj}50tS?WjnqWyO%^o7HS{7gT$N)WCt3KhpP-kJK=im!|X1g8NZg#jwt zxpLa?03B(nh(ui>Jk^<+>|}e>%QKkmy&Y%@&&>SN_?~JRBJ^K#x|N;6$Vms)AD%XS zbO=~LSn)9k1zDtr<@Ahis!&KWR0sNpG0@RHk=+7~|L*SQjhdGg8zaDif_42wEBe6t zHY^FFM`~HwTaSuLL_mxur30(gzwHK;PDNa-S-3Ti2aS#|&*Ha`%4bjY7PE#21}AqM z)PiuKtatzaL&H~>hx|5crh6ggkDWJ#%9nfyO~_j`jVzQjFH3H+i@Z#-%90XZ^ot6{zgmgC)5AP1h@jzl_Fgv&Tn7#P5@FQ1#Ol z&Uj>q>%@PJ=okH%{1A%&zfUt$%ohTIlN5n-A$=wQkBEo*9=j7GQ=Bm?8h&!8gi6TO zUda!_3PI|4t>9+nZhkistt1yDk?BD3rrogA&85cu#?1>&#nVtxcKWuTVujQb#ug?b zLJl7Tmye}?PQH;(@c+NIFozHLMpV+Lw5g3St~W>!V=)zrdH>$I`w;vOrP3IOz_Kb@`zOLS%yOjgJk-~Gk;`JZJPEQ1lGwlKD``A#55(5>^R zEocTZSxjeN@3RkH1Byo|%F8{yxu}-kz zvNl?7L0`GPO9?vO>q9#8aZgP==>}0x-)=sdzYkwSP>`X^Qokyt7b6yvxkeF%#wA3` zh`iSff0l&#X+Nf(^~*?{c|uV-RY2xQdhdej@?! zbQh1lD47gWyu3u{!~zu{{f7Go+Tl|9+tTtCPvrkyUtZ6(e#E}=J3j8I6=qS%6^SHo zCclc)gFQU{>`nE2JPAI6spNBJyyqY+P}3Vv+Qo;3@2 za39*3s!jcQqwRL();T$T!#3Z6Z3e}+|1+rw*M~cu_+kj_KHdd_GNsO~M!@eK?*Dx-`Yo>& z4Jpc)f-?NW$@?c32OpA8)|vI1uBVq7!SsK;%JHqJ$kwnK^QU`l8Oy*--*-_oZ{{j3 zij`Iy-7!+61s5AEKG@pID^gi<0w>{)#@cAtvyboy4-!C5Yt1z~+37fXzi}`(b;Q5o z&(tqpmF9qeS-s1S60imWx0`W<9fQZ3%js;n+1_N~-s*-VRUjrZKNS^6fnvGjWW2c{b(y$| zR_l2C`dHS7^Jnx|FJ+!lh-cux7RyPj-y2!=WiKyt+FwX;;mB2c_(d!cH*EHBGo6P@ zK!6T3oO{#283V`TaNcqPe)8-EQB4gmHq)&8g_`_6qK?C*`I`g4^k?($9 zrOri?*)9rxumREp%MAry%i95TdiTvxVG%mgKUF-Np6 z5;3W8+3=q~<9wIc7#Uw;7Cf58w0!Nyd&1S@&-*QY$iEVk)&B~R+urX@9f$Og%mIe=Q`9J?=Wz0sQ-S z74)*i4n^D0hrOd__seZCu^z8I+dP_wrLChL@NhPHQ0z_QDuXN04aG%wT8~JSfhX~c z%}&{J2+jJ<`58bXp%I)DcH4)&-AQP5DI$Sv{FDSQ!LQ}53KY5B?_)SB4y9ow>&3IF@Nt02Nc7_p!wdjd8g7sNw{>#d}}LSJslto6-lP%(&{e4M2vQ_Urn zX2P`^_ZG8&E#nKkzcaOfnNZc&*JfuW_p{JJ(XPVsRrf|BUe6}*>WZdFg^@7QvvD=# zHb*N3mr%)o`Ywa&)W?pR{|>-tuCKNBr8@Kl&_UNXoT@~#)doe=(}@_;dAsGRkSm%A z=XW^!LSbTA1SxKe`DoF31;klNO9w34z`87ZtSSj#nZ<3#=KxMOZ_Rc<45b@}_I01n z(2jzl+zIduMMlZL0%2sItIU{8f2{K~%55DS=;W&n=1m%wJjY@@g9EMN!1rTGNsN+F zqwV7#GQHP=*QAwc_JvDmLA&2*$&hWwflmzDRq-+3iSbFpX?x_cW@e>up3Bw!ZmMv+ z#nhdzO*k5ovaleKIJvvI2@Kh&&p|wS_PR7Jz&i2{BrutVNWShzgn&jsh2vfoVCK6a zam#u-m1PZt`^ii^YKLo6pn8j6}5ViDkh z5$BhH1w8fn+D8As9lJZ#L&&kOLq-v+ET*Jf4S9W(?>_~X$TNuB)EbQH8Pzi;ypo)j zRydVi_hUG^bd3;M>dj`nhi~UIC%}3Ai!&eP8fDvI&Wyh0JEoF`N<4@?J=AgMdZ^F( zT(9;5dsp!pU2T4E+Vy*!)<2cnkBb$Zy}k4_d<;}nGy1Ih=dy*1PNSY1eKF%^Lrj`= z9_?MQdgncCd{Pmn{iI*53bF8F&nW!zCW+Ha4A!*}74l6R=pw3@NFA?xTG?zviwTt% z+uKdL@eBtXWGo8EmG;1in%!Wu4TJXYZlQ310PsWZO7%=CoHAc>F;aLhj!Eu9gBp#f-<(&O!LKoY zxYT$QCj2NA7rT`2Ebr$X`Q-9kSq2NmNjmAS|GuK)t4^KMzqyS{*6QgnDR5}-QcW-q zyty~KxTRNaKAJ6;O8!x4{C572p};h`A5ZA`&V&!dPsAX7r~0P(jb7<*(FaM8%_RGw zJ~AFuX8l!1-*^y>S>r@ZH>p#$FvT*Uq=)E(zbaZvX25APkz;9n4vr^)qc>3<95gDC_}|p~qG*@EpYu9za8Vb}(5?ORH~~71n${u)Z+TMjSU% z;3;lA6TI^lc$BB|Z80n}pVDi+>Bu>*;%-2T%(j_-sJ`rAi-sZ7p zDIg1pr2a6;kS^&_c{;BJ3VAa)Ve#1U;p`Wrd{7F2h8s@)!0*mX-Q?65CVZ`usaRXb zC+R0;vs8Anj`8Q_?ygX~`S8;%uZVvSJ9+pspqcTU+!Z#x=XgWK!@3dk}MCG(`ncUtYqtklAj7|R$niz@C@~KV&dc1e4a;t z(z{<=(5e2s7?BWzWb%+TQXw7ndES{xqlS)ygNiRtMzi6i*n67jX}b_?Y?_Y=x1-sq z)mGDUy`wuF0a5*G1yBssd~I6N_~)3uC;p)A*J6%1e`fHYH@yei<}Iks=Wv+hh#y`R z+u7Ui@E_xXE*xg?NqHS<%w>>|ab)8|BVaqRpUn40tgcob)iiztT8swQgR1hau_br2 zhNyZ`F06r7GZ+3(1ay>&hO~~@mWjce)F0wdkdXN5`IP@?wz}x*c$JhurDKP(va!S_ z7#uhHRCURCe!shWwYc~~GSixMyZQb;k)`R@<*q+GK| zt4$hmG8hki#yc?J)AyhoDB)haAf z;Vsi!UIMPxQC>?EMtcW)59j-9z|qvGa0!r6xY&7+v9lWiXl`)ZL1x7a^CW*F{@fR~ zOZ!$|W$9aYzZ-}NPjLTmU+>^4O$`MpKf7+e#+qY$7b2v^qOu^|*2jk@v_9P48#_-a zA2dO?$FtSX{(L+i;+qvCP9j+%d75$-9?)noPG~u6bZ;ca#%=%{JY7r(@Xd{mDgwQ~ z`{f}K`?X_X)E|$Am+)zn4d;eZGQCI5#YyZN{C=!r3Yl7!-k>Xad1e^nMG!2t2BFhz z#(OL9GXmAmXB0Zb>>L=dRl&1!+*@v8woLkjrx2dCMcw7Q&eJR2fFQ_UVOtI63T%@Z zoP;l8Y)dg2?^GptJBheI?=DpaMg0_I#|mv9mTp1l(x?hIEhMB(d9MYi2TKTUN&$uM z(Oi}&VRT5rwbIKTCL=F4qGE@;35kIhkD zd7S?@_bYH9C5O*$PbyH#MUwJ0@~yk@`sAEcKo@8jTz~13D{;07Lc+?Z_RjgpY;AS> zW8$8hfR5M=Vv8k`*b47`fB~Wtzdt3knio+4?+++AySkz**=7exrE7;D5 zl5~d>7tiyg#M>S~yu83Qw(|Ecj&?ri>TPx0vY25iw2ai5^t+U7)!X z?ZL%8N<`)WeoI%M6N{LBC3bLWSQrY0f*}<{Oqv=hM87-$_V928(288v_u1d-Us+A% z*i;H3NpNjXrYP-L-r$%R5qTTd*16ZgBhAXHFo%76!OwtHWaRpIbcU7gdYuTvZ=VgMqxGl+#KTjgj!QhNFm#&XC|Bh$t9nKnU6Zyt` zuAJA2daqK2IVAD9S6ckPMGM>=0lS|35 zgXu!EaE!iAFB1_lOpcE@LYVxXJNnFIu|l9G}FC4HP|U5hqT zhQZI^DzyY%*BpXNqoP8UFv(g82*p}Xo=RySp-ty?DM|$AZV#_gzqQ#XO(`NEZnqEF zsX91XOO6_{0hE%^rp_7B-3O8UNzcpP?)Y{-sTY{!&ihBcDrv(515Q_VQt%~7aL8XF zhnAuqU40KiJr);l6N5l@R4u)!30);gA&ApcrhK!|NdqJGfwD?7fIP@en}b#!>0zSt@%!B8|(6kk%haH(l!fEo|V z_&sWVs6l*IdZB)0euoNysO5t_53c;4phe6JAA`0d3LfIw^qmAtGWt<~_6)kZ z#R?yIVx6D$3|^q9U4udTT=dfGiBSdPdS1K(GvT?`(scLMMDE}25oJn6O5wH?C2!p( zhgq9yfWm4ojQkxS>-u7AYtNkl^wdG3C>2kfCo>%@CTmcOF)PYSDFdROuhOZDr)S@l z+I=ctt-Z&#k@;H+73S{(xcL;{urq&qSnRM0ZlM^Ub$wRIB~{*;l`h|zWV zj~kez@x1sG4L{Fo$XcXlJ-Iu-`@2;cxBK_x5`9?eocRG?B~R#liNQ;?^=tmmAc(f8tW4iS6X~u9I9@tFq1g|=;&`#V*V6k_Jn(nZh6H3gYDcA}0EZztJRb zB$isS0-vwuGT&rO`)Ylo<^Iv5vYvK0w~?i{H3VR>xd7c^O%vvJb-OBLMVH=ypl+9S z9H+O%m7~tW#Nel4Q@{T*A@3s3MyLM%zP}i-hDt9id+?q|3w?aZ0+{QZQL+!}xPM<= zl8yY7&}WZFIIiI$GM*J39(>%$miVxlt7=)lSoBvB>OOVblA(DL;W6r|D{yAMAAj{{B6^y}8$LhwKrKe2e)A zw-%g)^>kq9;d)n<9F;9A=z98BimZ_Z3a{XY!yn(BRyQ}Fzl8ur$ZjQv!!53T-Z$sm zF7pw(>fkayS13FXKU-k2u8wl)Y&T)MT=w-gO!(W`!QZ3i)B?p^`rNcFpkj#pE7Vwz zjCecyS;^Dj@lg;3?~U%4?UnSWZx5~_3Xn?WtiO1hnC8WnW?bcMlRC1_dLP- zxi{j*hchJ^EDY-JU17pEly4c&7OT6}^RWP(X=_-U!}bxL%=U=%TOIaWQCaz4t#n%m z=9OMpIIrURB!zKpyiV;#_F7v{ip1h^$kGhTZ`~R_Pa;izqLbJx@KCT*ie*nPDKu13 zI$}e-nMTjDhmU6Kk3b;j#crqb!6SAAUB>p%XSLh;G-(}_WY5(#>VF-C+kEE{)rCg} zov!e>xKtMbi@Qr5XnLt_$m!)-Bt?k%vtmTGqE46fLm2l0>3MZd)CB}Q)7Wn%5 zHm3?_iO_HGsC@bqY0uU^K0e;Qs1w_7t(K>>dL`_31x8NIYDK_Vf35vf@=Lt$b6uKP zB>+dsHIgb|)JfF!%(|O?x^r5*4FQUf!-w7e$N)S1uYA_@6)EyfAXD&YZ68~c-bdMh zLDatVV&_E#&of0sELOd>eP2)S>S0&V z@;2JQ)JOAq?ojvSe_uOGLxZRFw#iMwZsix}9$+h>8U?e8;qozu!;{x99`5X%-0mST zsE7QXda8Qtzp4h}Nw`+HrvlBZZnFH8ACC+|VFUJ`fDrbL7RO5t0awCZ$+!3zr;J+; z@%5@*EKeQtrW}N*s3uVXp#dH*ZA2kVcHIqLb2~XA6;1{UPuJp*?c@F)xcp1xl|F&g$(VXe6U1_J- zT!qck#@4b**u`S$*Jb7C92G+jzAxzF8PQ6woVOy{e~|L0)OfBpC)YRpo;1hA;O9EJ zJ-j2BomFR*jAT;JCp2@I<)lBnc=j(odag$I~;(!su0;#F3#YP2Di#(^uQV1Y;<~lHS)sidm8AVwFxu1(S5l_i7&>deME~oxV z!XqF-M;uDw<#rvb==e<)%qtz~(a_ixN}$7?&K0xG{gT$i#BBJ%wJI?#-Dgt+QB~5?;mdB;p2N%LifX{Ykly386L;Gax8cuQmj$6tm$~jG*H@9s^J!&DzBi4vIM>c(HqpJm zOtipS#oE~#Uw*Q?mr8A_{xZuzM)tGnGoR;WCklw(wHf~tuoxQj;V(#Y7FP`o$XD$* z^ggu_%*wGXjys@?#=-PER*rf9lda2(Ud%r}S1;T)#8TDyXx4paEz5tqB>JV9@cs69 zT3Q-Q^KD1>xEVmHFFzf(@6R@S+}`|bHSn&q>?8#w0?gi>yF+QYtj57C zU-R!`{S_h#%5)yCdnPs)Qlqcj(QkxjOs4r%(waM(AhG21JzPmBPYlbfCO1bh+MYEHFl}b#$ln}`SHH4C` zUc$Qh$BFejI=E1NxBZ^6jSp$?DUgNe`{$lbkI`bDo5RQflw>BX@r z5OKgw!@3z57z78wy5G!+06GRCQC$coDQ@hS%l&&KR6sj&xOA}8Xl_kPg)KHZ+m@?^ z@V*dA^kBX@PXMrUi{y#20DW}5R1`FoKk@I5VAm(3M&0{PYLQ5T*k1|5hC-ENh}B3 z@V!X(IG2^unM`PdtJT^K>Q1DwpvQHZR$**_aiy5C`1{4{+xaTnq5ijb*XGqnGw@KY zN^9HdWy5a5*RrF^Pk%*m=%^uSHQc1SHl&_5oxId2PNd9qOsv`iH%K4`vBwYM&397S z*w6r|W8CDr^JvgSDAlW2gN%l zmSA_YiQXvMh6YR9YiAvh_?MAMSvYK`74~n|H)7!^hfJHMprRu1dH1~W?($!%h{cRC zJ23N7B9i+A#eHq8a|Dq=?vOqu971L?@&IIX4}x?k1Hr|_u` zzXh@@9gxZzlS!FNVspq3%yLlck%Es#8+v>dpSfMOkAW)uNx=O#>);GVHi}o^&VGC6vD-1-1k@Fq8?u;QN`0|F8S+m}*0Kv(fCGSBGj6%U zO~^`LOxtkN{n|(bf-O4PE1d0jbV&jUeh}TAS=u_&v`TzgnweUwdYP<0B3ST=AD=&W zkT;((z8iYu)~K5~%4It>XX^Vjum{t+G-vA?8j?#xrcPY_)OQmQl}#A(58|k@gOnET zaa)m{Efp8XN6!n18#2kt%C4`iW0=30K#_y3Qko4tahS=$Rz6=VE9$e7Fez4gs>V zKU?k>{)(>x0}UMl318_M&18s?Ir(l}zrVYVO{RAT>@U+M#ow<q0wE`fCbodd9OViFQji>t``~c#f}^=P|IFgg2M> z_Y2_5`16Ox`C!6m!e22XSs%T7Dp)ypLhs{sL5X?Jpyh-6;II38ft0P74CH7fKlAKY ze9gR0+qufLM4Xm{c)PZhN*+?lWHPz38_z)^5>y6!XuZnKFA;TdHW+!;0S(=lr`m%vVtcEvZl)h)V4g~D!KAro5^sIjB-4knt`~{rmw029iIz4dgM7N=8Po+L!iEm0pRi98gxq zOZ(nBHa%L7RYx_G-goU07CJ4&JLh$&0KFGIaW^AZ!YI&(SYTo)?trA2LhTCG+!kw^ zjSHB~MPq|xR^ypF1H5I-FQq!CMK{e@wk~k;AO8M9&oxV`8~tEX3%696 za(rfvvnz~<-|BKW)$%5Q_C>A4-krEIRd$9BJQlwL;QS?$R zv%bR=n~-a~7ihjcEhmE|ReEgnW%a|d{p#v!Ly6fM)Q?ic4O@l(yHv&_*b(snznj5? zKbxcEMgyhFzwAtM?fp)K?M5wcV^cbBW>!Ts)%f+FH2LJieQ zMpl7vksDe>ykclW)tTeAoMB}VULKsBobB&bGXK0>6w<1nrUoFn7A@Z6Ux~NY{!XOn z3WnPX24PBeh_y3*m-}^pu@OAba1S3Nb9`~)=rHa3BIna6LKZ$+P7X?nH72+|*Xn1NoK7*;t_^n^b}VjgGZq#zX`eRA*;dh` zzrWQhf(z8FwB~9-gw9;K?Q~7)(cN7w&Z~ZBE>(J@M%_K*5gGRIz+{H~zm{Hm{c)H? zSrti0VP1VACc6hmNHrBT0jKl~aQbu%Ow!GEHYaNvNJvP@x-Q@XraS>YMwF|Udw_CQHfb>_NibOic~-W9F9dY8u@xYRlTJbh z_nWY4GCq&r&c*5t@Phs~RT~sok{vQwl7NII zPk1j_f8}O0q93$TG$9*b9Tysj(b`Cz8u1TWqB?0PCt<*#WJ(Y6<-r20gi3d>ep)mh9xmLKALS=3%!oeI)87TK=Nz=O z)1#viuWAy@q0x$9mdBUOOiTOf_+=2nA4KD~6kNCBrdX0tV>%`VpuRK7CtBC^8PUEs z09$I8uOD(ih{K~SxUH|WC zVAqhcdtiuyC|IQ|6M6Ass#Jedjm_y3%^@Zzbw2Vuzn}b(5p9V3*9`YhR&r3G=F9Da zK|RDu%P9f--RRnpK-cqz0B;4c2ncAx;UZEn3B=ENZ@m0xi33hA{^VjM>DbYzFAzGU zN) zi^MqYkb|%yqoBAvqU(uZifq}@4x>^-uq2VyL$nm8mqmRJwuEjr5VaHBsR!5!P;~K~amI@~BvyY#lH(dSSzIHeSw_>9{ zf5A2ZmxgnW4uQ%_ru`*wMu5VJLVRCB+h8a{6nvnZR0DCwh|cD3jF%^ATzPoZ`;aw+Dyp6CHSFx363ki|rNyxL%l!j94cf#LZ0 zEf&Y+4Bxa1ztz?@Hf9?fAotWJ=YhI&&-;S^SmBFiphd=127$(Y}icw;o z%Sx8adt;M>%eDjI%iYak1d-cz1MlN&Agj8!s zd<>tXlBL;vh0|4xf72mjWSD7am@)Jxh5y^8tf?trY=4LU_M_qTzA-CDj$W}$j0~yg zA-T78Q?TI;BIkS8uyP#o|p-)j}*w=2wvn~VFHLD-t9XMPf)745|LLK7Ui#OwfNG;a^dN0zBKS_{TWYcnyUw5#A&>KInIiM@rQUqqnn6r8r6!fKCJ4HNm%z{K}I z3&J%Ylzt1$OJPcRO1V66Ifz{K(CMGSOG9Ke%@Hd3DjJY_)uu3P zMi5isD=-Y&oUkc@;j`$mt-J+(U&ROm#h!mRYL5SuaJM~S2m^?)<-Gyf&;fGU%(dA~&*7G__N*NkwSLgQ9kr0vC4IiI2_F~zZSDhFj zHbpw1)6jqxn3YOi{>P`rYGYJxSidM_)HS@vZT&ksZ*o_+QP&exPtuVtDU8g_DZEyf zK+X{jWIoAyyR(_~k=n)fn8rjipPeezDxDU)X3Jq*i35I-KVA?8LXFkTmJ_u6IAs#&#Uan4X+R(9j zgduKQMS{SR4gPfDgBlcrObC$n0|zaq%~kyo5+#%}>|Ij&2m9~N%ZCB9k<&8zxVcz} zJ5i!g?JevP0I91^1?1^Pq1pwp>F<7Uyw9;>n>~ET3+5ecX63V<*Q?!)?pNS16cYX_ zwnQqw@Uh_2p9~vM>L-E@=)V^0F9GHMDJo33Z>Z_?(ESKx)n->3T79aE?mcTLFaI!W zzDR7SvUy4Obb5L1+}75%^zG_RRIipUk~e0CM_z;Wm#+m_XT<;KvRWsQ!n{s}CERW4 zr=(taqn^9Ab8dZBVSL%-5SOgSKCU_|Aps!`WHj zRPhYHcD|{2*2ufNu=^v}ORz-F=Bp1!N0$Tl&5Le(K$?oxdc8l}Xba+`fU@#ns_`t~ zWv}udSr8y~p7`4Mruo1^wA0Mg^xI=+9Pm8>)P)t7CXFZmPJS_s$K3%SFh2eb_{-9C z%|J=HR#=)zpY_%A@xm8F=$F9GeE3Mq!ySf+Ua!q)18=vzAExNwV8U6sTya=XKDBhD z;W8y6t$Yf6TA@}IkeA~dNk=v`G#CdgIA{3CbW+o#G7(a@MlS#$?|!caCT`c+q5 z1tV%Ye_HxUttT4unq+!rUO$NhYa zLh1~|EmWTtRDbM}Ss8>36$Vw!Z1ITm?a{^APR&Svwft5vSW#r#b9Cd3XtjAcX^ETV zWuL3`W02-VVE6=rV@$B`#1Ew6|8u1wq)Z;bL@}B5hCo^R@PWl~ckB!a(*T683?)fx z-gKve%V2O|V5!o|c^EWZ8yBRj#KdDp3ikIWt)?2zm1r|d|JENhlfAtilo>Su6hz=v z<3228v#i9##)^)L0#`80V!d;aKlEmOSh&4SOe%!9XbMV!B?ty)e7y=0IkTp+(7q@-_LLPz6 z0ElXb?eQUiH_LF>7Xxhbe93Z!iU?u?!*{ng$HZ|-@xS8YSOV8h-bss!wrUmrSoZ_x z2$0nKcY7P35U=k{05aF$T0Uv&%#MF~u)Lh@%tA(HD~bsj1*te1OtIJ3b5IIJ9MDc) z-B~42=G;AMp58v^11B|$@y(W%7CDxt_=FS?RWHmSY7Pt@fUG9-2j$ND-!T2(?$Y&k z8>Ef%#d$6nmQECC`p)H!n%i@1TTTY*)SiYVG1f*hNk{%YJVb}kM9rp+^78WoJ3-d( z-xG!E?n|~`K|Vz=(k4(%gC4~MVfycxf^iGWi5zcC8KCujj>ZQ%Dw&cYDwgTLI|`<) zz;Bv>k0LsHVx%OkY$fXSuEh*^w}Xq<_|ATFwe%C zQ=&OnB0&F)`Ud>OAUQd?$bCr$2%a_si4GuNgd@_!Q+L!G0fmTD=KXu~nM^ZxZUW@^0tW@`+y^gl+(EX!J*agqp&k(-U?(VIb zZ_BP*$&1NX^UJ&-_pwN$a34tZZ5QiQtE>rhreXK?CmS98zy7eHeckArd_Y0>6nY%* z>48e5^FhADn`5(n7fV-YX)f#jCMSp9UgD%zPq6?G1x+Fo&7t1GXQ-%vC#l`+`E4wu z9xvZzXFmo31)UO+L=HuqoR6>1kc&TwSvnG^p`TGgDa6uJi<8%D_V4w#Wwdsr`=Ozuuk`{A?dx zNkG0NPX)nQ#5^u^%R9(u`Wjszv2ep8WeZ7^HNmf4D(sDJ*>Bf!)A6RH`>`OBFfw6u z2sknNi7OgozBNB?IX*5~W9#e=zTx3u+8#;IJlh;BR?Z&s3rKz8X*2>dnD9L6F-7!^ zg)xXnVEgf5_xJZ2vABq>?i5E<5{iFaNg?FWhFptJ*gbANwTp~eOnchJ~;0;+P9Nz$WdX>2K z)=)7RnSnhZ`1&~WJ8Cl#-}%SPM^l6G#}q*ncJ{Ztp_3eq4!M!`C-%ScaJjSdGBs*O;=iNc|qKbOG`RBI zU;8(>HgX9wc9#ltqx?k1B-x*>pB8RuRv$ie5>7QNRRo3cG63c~xslRP2?#K8RrzIX zf9EH4d7>M8CaO3QzsATL&xkL8dAiirepOoz)#tF%gzqxU03wZzr5^gNf8P% zvP+O@-C~hMP?Nf9i~+uMFuC+&SC4OZ6%3ZWP&^J`1nAumh(XYci9h%HvZ9N-^ zADxtxv3KhypY5M{*q@}yw<@5W0efI~CIX1&^&&;~lx~}s&V}DUW)cY?*fWuae z8JP(*prRBIkSg?>xxbO_e%?u?g~C7JR;2UopiDAystTR3 z{LJ0V00<~WGV$VOy23(3zaSI#hh?FX5LX2gGE9+o#7VIHoo^qOpU^f{)KZl0G%5QL zp@+NPp7YWaykk~F%YDa8G&q5Wc&F|tFCU$gvjnC>>2)^7kE{9>_tpkfd!k73ad8hl zL`waxUKM#9rU*3%D5i0lC_21xfWvRf=iC>)uCwy?TNb`p_o)>AmnG^x8oyEVPzwTi zu9&$j$PQMA_a@su$Rz@xatQznXj z8w}5CWRSaE4nD_v;Sp~{Hc&uf`F!|j6HEz#B%IpkK!1k2P?SoJ=(5s$Sj^|*jlZTn zH8r(%yqPvZ84iTzrD(7bnM6QNyZ_m^3401F0~?HU`K)|S+RD*O^krFiIQFjJ*)suq z5F)f={gCa=+qbKNi+$S%%_wHc;IC+@w?H)?tV&Q=a^j_JF^IqE-%Op#MPV*MX*aSL%BN%VmchJ(ka{hDn zcd}M34dLsJPbK2va8c#1U@peY{9*Rj3ofpnYPi1FWe+%-{CKTgiZ%6H5%_s^%Hjh@GUNDbt@jz>B{yab>Wi@NPz z057XU^i?-Y=_?2n{eBszl@o`)aT50#e`r`3s!Tb{>{Y2cVcBl&82@~Y*$z;B4t?Ta zJ?H`v?80r+ck_)=7NeOU?o~rwx7n?nm{nsmOBkF%AfFGs!PY1wDX<>60s=Nd zKCgY&K_$@9cY}b;1|ZkmZ@X@L-RGuRxj6mJ(z!gH4?SoPuXuvqlm1%(5-CHZ3Lik= zJ`)(d_0r$sF08j-h)+y3+2p5+dqg5Mz3xN+hhQLJDMN)wmRrkr%>FdPeblk}rQo|| zzUSwlH{g=C_@7TI(NIVSI~6 zc?C9xk1W>UG>DJSYts0!q=|CX>w|TJqOL!@|Hy3c3ljA7NcD2Bv-!)(QcVz*cKoyO zdn_gJ@U5orUF$U0lA#z4&cAQHGw9ztod4!n07$x6hle#~FnATF4Sxn5s~Eq|Itwuz z4hOqIoA-IPaZmnG5__SQS1{%&J$=`!*tvS!>mmgmCnsm5W~txxXx5*R^sOeh?UOWM z!xtihkO%_KcK#Rr%U9UUD>%qk@_E*DX#nLx>6Ov`@q@fE7xLtA)%3L2#3M#wa^bs(6f zC@wB8X(&q7168*m`~En(uY7HN<9gPHiHF1U*CXMfNV-Z(K~~lwQ~vAOmo>6Hi;Rd= z`Ih`mP%-{Xgf>rR#5FkrSp2vjndHZ`PcblTXq7cNzX}42HmKt@)XC|Hvlk7`kBpSJ zn&qbzs#zC(3=M=xhg2Dw#`Ps8B;+$Ab8Eu|)S zC6^iG;>~9MZWORgLuI((b}Fi>lz0{%9%OxvTTh6#6ZwS~BmCdCswDn> zFTpb#9|z|b@5o*B#`_wRaoU9D=Att7A^n&8kE`zSY zm~reyrvd!(g8S64ihO1WL|j~$wqsQpyRYwU4LaJv;UOC2%}-x@Q4cI=0Nav#bUrgh zA4uPZLtSFXNUuiB_qzxWtT^o4J8!*2xVt&cYiCyP);Z~~`9(x1XmgQ5v%Qei!okKBm)E58Px#((pGc_!S3- zp<&;Jsb?`$;b^>%{_pf4h^EkfO~Pg)d_H@Dt4%1{g=N=b_a<37)^k`#spJ6!qNeWs zPDmCO|s{fn2ce@r}PeJevT9_;)s$$)A3Q+sPH6q=V`axMl1 zzep6)k1@Z*TOL05`5+0${&oD=i84>|Pw|kWudI-@Y`N}hCHn<~rYR!q5T_i`)QTQ^ zVk7@gA1+c&?r;vT&f{+0p!$SyR2MvU#Lx=`rXDx+^ zUhSqB6elN zYCv4zT4**+>t*fRy<5$8JW(YFO5kV9sE~J0q$fBh(ZJI}(6g^#H1d$4ZrcW3TY-gS zEJ^gPlS^svyml=03BgcFUW)%MvSrIth#@-c@!z#CS@q8cgb1_{UoQT=T)XU}AFkw& zrNG0aVj&DD`VaU01-9Zrhd0GzY~aE!7e0JleetiE$yR(Q@jQT_BhvaZ!~PEsdV%Vk z_`kYZyhI4*ivI7SABy&`25`c`mU51A`j^f_H`GLotXbv7}5UkP2ElBOCVsgFF%1ld*{fmo4uh;MKaw> z+d=-(tOs_kz=XyPfk9{>82t&MTz5M4S4&6;q>JX_nDWsNuI?K0Qw>#6P*O+6at(31 zPu_Y^S7NkYZ;umP7q z@+0e(Ko+(k%}Y(me}6x+jyT}n3Fjm2a{9}zJ)MCtJnAOj>-w^O<#=U2B#K#`MPJ!P z4|QKF1Q|QT!FM4pL0+MJNGQgD-|J3skGQm~wEON4ut&DDx&*>Kg%pVM`*Vcd9lO5E zDTdX)zCHsqyk(ESz)Nvws@$N;aHZaMu2Q#^oSO6;9<@PFPw(V>TyMM7=6~PG!K6UN z^T)&>Lt*0XC+?oW3kU`TQ&27Q#TEp<4FR=&lDOnr6Y29S13d`+FN;5AuWC1j+uZij zjgEUUWTj<*$I9wRr~Q#g@lJbxZioqjYG3n(Pe*X2$lXQzpM^dOIj9j?l9K@Ai&rmr z8F=aFS`O%Ll%1JtQW3CZX}JM81Np0b#XX_Dxw?DXb9mm3P9V%Gz0GMIIb2qTc6;Tv zcNa;(Wl-!F11iMmXXjg>ez!tZ+^&S5zotVE#DqNFMpX(Mb$xyb7*tF3tLuuCYUs2b z*lZ>n+h;=n3Kd)a45ciwg8nOm%=fQ{hL!(!Guw{3BuY;=Tik2%wAak-t%^Urw>nS3 zwcTW#&a+J1xWeB&KW(YZvAwVXdB&NZR3JDk{_q=-v@&22>0l@@M&^^@tG#X`^`Mei zb`bOo6v)7c`gbBvrPfNS`Cy<19t|-s!%+Ag+(=K{R^sa7V0$ zIb2%aj1im%BH0E%FNR4Z)vNke(O_D`B!~ca2S~sS3v&Xy+!J`e72lT7N3+Yku{(oa zv<@5oS~lK_AVG$7DF|fj_>Dx%=uW!+cWu*pXk^*jghz1G>QppHO_>DrF?wlP(C-(i z!9B)w7-Y2lp}hNg@}Wj-8iZ4U408Gl9jxnw@L?u2!eyt%n5QJ#kFX!!G>Aab8RS5s z27p6Ru4no$EaX&xiMM{Ur0dA*dAlADN_fc&1{#_lAh3cG&txCCacq~MOI*I~_KZrM zx|{Y9e4iQwzibeBi$-fYxvjpnwN;7hvo~qS(R^&$ji&(pa&NOoCF&)gCbZrkPd}m~ z(s%6R=XZx+>pxxN`qs+I3dAUPTadf$%)Vq}i#AMf5-?jUiI)tN)YISNHu)1hVWAiz zD|NEHSn7IBT5r4f%6oJdv=RXQL4mM|uW}{|-A|uBeI16c5U#X2S%3of-r4R~+yd8U*9L?C`p!7S<~EX4IHr+b=hy4rEFMr2aZ%fm3nQjN+c$Kz1uNIvUdy9>4MY21tpc7VnL z5Nq$h`2hGRDk_?_sRxDj+15g_lzar;UuJkXGd7saX@C2$@cu@DEl5L;>PbjRDP@ivFg;=d6FE_;v~O7Ol7le{u8u92Zwq#{J;TR02AOKX z*;L+f)s5o6sNY{KJU`?l?|W7sxgN$pPEw8Fqdf+;FO*Acz*WZy#?$Sq9bDm2M5W|x4tdjdM&m2W)S3dZ%u>d zsOfYE6Zfgh?svy*Qn%e{uAAT$1H~+^wGrBZ7XuiHl)~;U{F`-v((TN&=GNQ*@LAWL zDaqhRFLm06|5!I?Bwwwk7~uK%8mj!qJyb0+Hj*J;X?8knIIU-|_V^jFyfEp@Bwm`l4rhOQZx;E7s?F&33k9i=qPdGhL4J*`nTr z9M7?X902M9oIvO0nu4c;JanVEI2<0g>2Vq^2RLtxsw1^t2(es&qs?`1@$@Dns-;5$ zDn@y4c^`O-PyLF5ie!{?IJ0uYWpDg+c;>jJf3Swv<>H5XSZjvUxt*?V8Qajm>uR>& z|NNPu1L9Pj)xX_U7x3BR{dCqd$NLy#|LNs!%5dwBC_SRD__Ay0lNbchv({ zNrnF1!I!royRj@YB z5b=b>6J^Tcfiiv4e;d<>O)U=%fTjBa3c$lae+OQ8_zYX>u*gu2c_~dK`kk-saCBe7 zjO?4t`cRGZpJV13lnwE)M&QO}mm6uK9OpIEJj1Y__@>iwK8O6d zJd`sHqgL8b`>a6^3TGY~1owy@J1OF$k+PH1V6{nci#Wz=-kq@NLl@Av5zbZzwT6I@p;a7uO~kWt?o_r+kQt~tq-X=3vr~W_N2beR;Ngku?DRM^Swbc)~QMZ zRNY$?m?0|NTM3y$5I>fd#CdpXC2ujCflT0=-)#rlgW8&!UjMt9T*+WJn>jEb8>19< z0geEm-1IP=wZXDUbYuEpvyvPdEQuW*(9N`6R7+AHU?-3>yqh8dgV zKchO$UL#j^J8hXqg%vxiVwCP1irf%GVBrqnpVM{C$L%@yAS5j284o)vt2Nksqr(9n z&3_mb0X8G;R^!QvYhd-RHIC{ z-EA)%UuNyky5D{GdAQ_I3Wx9NslZR3pTcgN9)~xLZnkp+c7VYv;z)5>(esuhD*xYa z9+UdDx*RG|x14u^!7M;}`yz+y_k`QhFV%f~ZhljfR_X)S#;a3$*=P~R?Tc?pz10-X zN58(*Sxkhu`{)g2r?pp-?epL(=lBjafF);dZth7$9L{ zVgew9^nU%NYfdDLCe+AJzt!*f&&wJH%~JinPl_Dc2TZ~ z{3LGB=tV3k1qp<))EvQwNQhaIRT)mlVE$YHRsiIut&2m#EaCPis`;sO)*9h)dI2nTYy@pT~j`a^#Xa5s+UJiLAn5jimEjZiwIl z9!6|KO(5*cR!4zJ^Y($hSZoR zn>F`Ez0Y^&8k7=G!6v`h=Ibrdj#fTf=ddw=)9tc#GYDq!s4~4;_ocp`MSP$7C%$e!Vp569w|aI9YpAk*06o1ltWWwtLZN{cyJhwCa7dL5(E&frN(4M41SWQpg4PCWG)icaO%G$!NW~ zX3ZNYdz(g^%bJN|B>~@x_%dV6pNeit_t;QcIh9hiFTa3G6EKYSZP*OEyS?tOIm-QD z`Wd*03!9IoXC)=g7c1=}5Q1_lcRF$%WQ4V8IoGE_pk4d=VVsWhhyP5?dJ4PV)yYIP zTlZEId-_G?#%Rv@NDzQZf0Zbob_90Yx89C^b2QnWDqnXT!s#wSrY9yogDu-{LMZwa zvbk;7Bh_u^>Ig9IGA@_&o85eY(w%JaX|1$~$3Z_}^1Q2;pSi-_v0Z4nEpJ##<9BsB zOx^5q+Dm+U52aOCm5qDqef291;Fx9`Yccmea4W0X0JK&;_O#WR`T6OC?%RFud44w9e>%!{cW)mL4JBD(-IesAdK080xBFU%r(z>xy}4pogrE>dG6KAJsMm>_^t^sz)Dzg(=R=mQViDbjQ`SI!`4kBt&GJ4K{r7nkt#0(;GWnvmQqC0wIAG?GJ)eGEP+l=ift&JO(cNk z)=tCA%j=E98yA_(wRL@88Y&__%RatQZ;P3qOK+ zKVO^A9DBEE0uNvq$}^Ir7%SIq_;Yc%3gB5VPkO~TW5d6r)vh#UB;h3^ggBUQbmri- zSAfz6RN;8`;BoG`lj}5-#bb|u&RONSJ&)Z}$VXbu8-aMQyeqboC zl{~3-T3TuT7r2{(`3*<=Nq+tZ|JLQsyFCTM3jM~z+jDW3z1hoC+FLXTn2~txls7ck zEKXItW>qk%&}%*HR*Ew4`m)-*---p6LR^Y+O_hzwHxybDj1d zNQ+9bC3an4nlFn8FlfFaGfDhp(CoEy2l8z%jNu@@69SP8L<4%})!H#J!e=@`>*HhA zQ(5BvfCDa9x2^dAB62?O3`m2$31&l&+NNYxUDegYaQ*R3xNT=m14%i1Qw_(zFEwTJ z`rics=8KJ1d$v)k_aLJhk#xf63O(WWpL^1|q(oT1)QkoF@1ya3`<4i1Au zX@2mE^7GeAMQ9;tdZ$+t0KfkD=m`8vi|GBpHsnB&n4G-suyzgdUod9PLn^9%~jPyPnG?@eECv5mKhrxiT4XR1b6&gIcgjT2+?)jtUS6pQ zS11kBH*iWqX-ngXt-tROy9;LR^H~xklML$D_{!lSobFf8%{gSj#F%$wLo-j1T9CI_E8Q z_yHyevmVj~M1n5Zxi>H1V=07oco=GzC4c+~ldp2*00Mka)x}0cbh6|X5n*49HL}OE zl@bu-+2fG&y3Ica4CvbiXgvqFY3@rJn&2H{-Gj5Fx$ zTRgawc@N1FtE#G|r?sMbf?T7?s_r|`44PZzCYU1Mp0BaulK;ZT$6u=R1|E{IZ`PWX z2JL>WUahtMqAv?PCLIOAxf?};RiGM@<& znt(|lexyva|Mw=`eyPH?ty}vd3NSVMRdw z?{#cy>hD(X(* zrgr0Wcd}V2$IrgyZnRuSz%%f`7a5W$fqLC~1E+E42eU z&7nvb%%xeBkV*dUVgU+lt-Ff_8=0?;u{t3TeM1iYmS2_k0iZT-s< zasK1C(69uNOTges@wjx_U|#^~cC6uS!SCD@72EF~06GUtco|I%t40O+)!!;G2t41K zYj@9SqmI7de8IR?dr0Vgwmnf`u`id%REku79A{?+?yywF;+Y#ZS1VM!nddt4Q=|^D zwDJU>9*O{-+2MC~X<<9ppx091=P}RNl&2xvzG#82NZOmw}nFGy9#nux0 z)mi^IJ)jnk*6-=G8=Tw*Ngo6L?)sreqfE`*-x#y)j>sd?xDj@?ol;S-A8jg8_hGDt^+yqf7+mazN09Ttrtcbbdd z(D1O7$P;No2~NJ33d!r>+vrD+)A8HtiREd#;$~own&gFa)LEl~(9w6>+CqD}Y)GO= zS(B48=^BY;0^I-3D--bvu_Syzm_5x0F;Gva;*yfHM{BD3sjz9+9xr=jb0id6eZPOh z&ItB!A)$0qEofi`AxKJJUS0;-H84sVOs(PMmKKq?K9fTN-!gOQ@l3_AiS^;p0V6U@ znBec=b1{m;!12fsm~qXKPQxyUnOyrw8~pgZh!#SF&}A2(Wng99xy_@-CA+xlKH6Z|We4Wv0s3ZsIaM`{2a{O>#IW3ZpJnS`AyBQ4+i`Lz{U>OaWn zUU^6K5N62#*m|n$+G|k&R1ImIE4Ad4r5qP-c}127`b!_AmFMjm9H#E z3nw+$&1Giks|ED5E6#Roxj=3R*{U_F0(1x--@{iPV4jG3pUuzN8@`+*0@%_WJ%LI`6~wUx$T=3j~-8L{pwe4DEfLl_$w~1I^~~Ufj;nl$Dll{qg&u zhAAKxB&Xz@AE)QI5?^2G5CB@2K}u$(ZZ*88qy$>0W z0`xXCjW|^R0Td3cvlz6sY<6pwzaOy!l(fd%*}{x^NbTg9%hC6=+ec@BVK!B+DnS3T zS$2$yQmD=4+T2N^1RxmLpx`?^Jq4t1HHLD#EH9Bh4(%oe)|X)dJZ~Toq!n#Hn$!V` zW~O~tC`N$bYHDig<}`G?3yl((V1HkC;bJqP2!NquNI;$jt_a3@yoDU+ zr3eGJ3wj><=gjl%??+g&^#7VxYyF&<2*lf-`ZC153pfots?I(S%9x(D`}Up1a=2#l zdM3omdC4>}hno#0)y)o@XMFS&@IFnS45IPK;g;bdlpZJiO_)z#09Lc-T8E~P;~&edlTuSd^wHnzA-S&`SRw2UZ($GV-*>;08z!lp*!tDlULJJ7!jd^A}`_*$W$x_tI?E&KEWI{z-1B!-3l3xyj3xJS)`AyOH$6VNa zICC=3_xeJGYi7$+M*UGV7Y)Kl0`$~YR@=a19oV!S$#-7?^p%p&i*2YzA)Vi)nd+R9 z4grKXCegGQ2uP?Tpk3dzUr=ix0J$CmB64l>`g_-(k&Kr*^&+e#(A#3d=E_w$CyancA;jSb-@6GF^jeO>;G zn)fwo^P6$9?V=}p1FFw~<4*&qA!L4Bw%uN9B~!jKHm$Q#SBqP$RB8tp$8a`}Fe78( z`$?;mCxn`1x}c#unhij~@Bur4Z^4>?vz}66BAV>#kpX~&B2`*E4qcy3>3#F4wwh!C zRKe+bUma65Qxvr+&vPVq5omW97)Gl06wb?T;gZF`W!s7eEW`)O~`*=d;aF*9%O zTpE}}14dJ&(jOaV(aCWPA@=aBXTlp*? zg@$@dsn>;vJCycZttyEdN9%n3u;SL3!lp+jrvmaP(9nT~<$bI9DNr#9x$hn6P85L# z?rkzVf%M@iN}u$l?#MPD_)rdKHgXo4TvaE(f~m`&jSYH)i-JJ*W2!h=b#=Lf--O11 zgfD7!QgZj;s{Q8Y+x&sAV5dBNHs$WssH$s&r9S=v3R`b)32t0)Dv-gIl1v50O28y} zf&Kh=OkBWvYJS1{Y^p-fdb)WZ5fbukfxG+0Hq_&69~TWB25rf;^jbN46v+mZFg0aG z+iuOk8#mOK%W|-JACFX;RzxIU2Q`{ltho9LPa=S;T1%=Qy{EuB9h1ONd-P3d2m}V@ z#Ei@bj;653kY!K(30`&e)@*!rWi&JqcY`kq(bY3_6ylCY-T30aJ+dX41q9ANX$rM+ znFh=`=49GRsaU0BRuP0_x#~@T0#OPPF!vJmMRn&CWpKR`Mt#1wgiq`9x3Op>U9`b` z7}LnIuz!wa@As~ z0b@u2$ay>eJK?`ovyPi%d%(Qap39QiefUSmzOc{?iq0XYQoXsUU287l=Pm=?;$CdE zP%<<$RB>{$y3(tiNy^CZ-o2mxnRHZ_9rdu}F+{|ny*oU(h z2hLT^rPaB(VHW?bC`^7gTTcPyS~|IHeSErBn%@tH0(}4%rd3-c7XgMOiWgFHqm(D| zFx#c!CzvL61av&DHK!19Ik*^D#UL!51kPeHcehs!)(bMp#L<#lKN&Yh(}j(GBJ`Tw zZD(5|HZI90ty@n2=m7j{k`a8}m6f0pJ^t&#HZ;5ERyyfr9}I9WfXZmIB^wY0821=F zyO=HEDqvDd8?Lb_jQkYZ#`D#y&MJYynL^m>*N1z^J!s$*(uJO)!>Fjl>~_Be-acZk z8RQ&(ZX#YiVm>vNz-6TAmy#0Z@Lc3(CBDxBLpsnX7!H2@0)rbEEUkdv)w*AYDRFB*(kk~|HXUUL zBkGIJgz24O7lI?1U8mmq{BKfHQg2MQ??#ru?LT*!cGaug;-N~VItdFdk8pC)XjRSz_g}wj! zgq`ilGZ9MBaC09mcRrAj=7ERjmjDS42ta2dR8oVHkOO*pcD5wVI4>p5$IQ&(8hN0G z>&>l-*HU*lH0}w-^99*rv})>gT8GBS=*QH-TqFu`hj;JZdo>l2Wqru&UI8wkSdVeJ zyozZ0UG{IcK@&UQ;tAYF8svKrjs;>3mlqdTSKCcsinp!4;}WgY_5Gqq~DLKO7R|%I^AE^9A+Aa3)z+t`=l}ZOe zrzDl?BvCBj?^?hXv!K_*Neqje3_b64XeTAw4h)AFMtfFy9u0Dl=ahg(jGb< zinW=?y#=;-7$ONObyn-rk4f~}9YuwO=TLv15fYlzrHfEKq$MJtsgH0GhX1vRQRtSnw{e$P(<<$A>xiUbgR?oVcYpSpfRUtvgXwWJ$K# z(a|AC_#BUlSCEmh$CMusiXLIiFgCnj>m`=9M%62nr`OpnHQF9mGcqu2Hnmyxuw<_) zFeMgkb_Pl=4d4FxwWSK^5P(mh!npeq<1@3>hYy=6s_lTDWWK$imTXc`kl)??nn=2j zj1@@^?ta_BuNklHz+@l0elV1K8{o4=;I_AWke2n7@@mv@E#eR{aWF5)DgjQQyyI3j5asf$fZ;rnwBIJ3LRBa;jsZs@VGoWNy#n|}G zn=0m`^dfQg;+R$7uLcNv7!W7~GeP!YT+F?sh=|AL$GN>UAu|;hm!!T`uItpc>$5O0 zdnx`r9}|n)pf$`k^k;32o5ENy$%(%wK4;8{qNBsSHY7AOQ^>V<{iDx=a!3GRvXml? zG0MHHwTNCY(?&8Se$r&5pdw($?uuA_o*{N$Cfe}`=J5ObVF&mCJ0x<&89Y$W+8@(m z2vxl=V*_HhKine59!XA2Ju42JA?S$Z+sHwaJ2^mi41LZFFH!q~_-RH6!H{@VQ`VT( zE+R&NkfE^MQemV;hdj_8pSPQtBV*;}egZM{AdlHC+T3K0j%Wys9x!FmV4LOSB|2Je zeo6B76U!$W3}j>sT8thX)GLetZ&V&%fj|@(?NW9ZM0ph+kcY=cvQQB|g#6&6k&A55 zjRVsQY>-Fb&LSoex4;!1qpjY2a5ikr1fTy59kTUE+73MtNjB)kF z)ok>YEKYaoi1|CiTr@J1hPQZ7C=?3|qiQotg_($+gjtLh(RIfS#a{UY0t~kZ`l>lv ze<`a11qSTG(WK8XXv92P6($1qGzxEj#yRpYKbpsnw4HwzIlL@>`LghLGI# zT5K(8ma|_6@Wt=Gf*pgsIt$(nFUZd)VZVtrdD<}7>OH0p%_~Ae9%Nh0eYF!kDp015 zz4<0LA(O(+lr7}>ACUrJ*C_%qTlDjf1ejn*m71A(G#Q;-mqXUtXMelBa^yEK)gB(WbdCJ}Z7+j%~fM5j>+8&d#8$VrhYChj>6X0@K zD#Rs{HqutP$O$nCc>%Op2N-;xjR0u!os))WrV~edNw9aiqV*9B9-ZT}f}UIBvjnUr zw5PCWap=i}qYn|_c%9aY)E#G)AxtA`%yidk{Lw58v`Uamedhb%bEhSG8Fk_i57NAY zuecngb6-4bmm`a&MXy-mJW(ER6HNDankt?#Qs7X3sXq7GFl2t#%kTv)1n!9-ejNeD z_it2vr2P~7obPI%1+uuj>%Qdc4TPcwBdJ&rC_euENupzCXJ@Zs7!CRPKq}Z%{f|d{ zFuC&ODEMtYfVIKc(8U>DvC`k)Z_E`B)Cl}c*&E{Imn*+44HVOPLtsyyJ-eRD2==uR zllf4zrXl2F!!dM(R2Q$*YZx`Ib1NoOD zBmhW@ai88aDIe&^Pa+Z=Yb{1HMa;H~b9`fAEE;9g8>`}vgdUj1w z1;j&N!{F*jwIs$u&3!5m$qK@%0N#3fTGx7i=M6f~6A#nG2vR9pAQFcj9w|~%}E z2UP8};#PfrC1VG9#n$P6_cz4dr*tzaN1(7HH{(ln&6})!v0mv*YigBtTReO#;d}S3 zP0b{@r@S6bx9x@6trpGMjg|Hz!j7tCnQZdrlmAu{wK4eO_}i7G##G#j%c_;!5<{bs z&6KUIn(~oGd3h9l9wL#-un~`x)pzgT7tQT~Ndo})s2b2aIqE68@6fPAnrSeAPO^tA zEF|QqTY48Od-}KCGT;=^0!|g`jG0vpzp;%#E&^$N)M>N3D%cHiaruo!TZz}D;x*Mp z`S?B=y?gV9IXIVrHRPp|5_=XQ+#~w|tf;W)MdTAABGc-~cIUgeDD)8hcx;2}stNZS0C@{)IUCa8S3F65mcI%CQj&967b2NM^E?t`0)-RK`>SsILCN>aWTEu1`2^T zuRnw=ZZh1mQc@VSpWk3Gw2)?!6A&0l`LmFbw*9VbYKmWY2!UeZg#w|?+4aUfGfuN+ z{F`U+Xhl@C5qAnhh|x#TKDs1wwH$v z3&{P&FKt2Mh@3guA7w%yGEM56*4Fj>X+q}=xXtSZkjL40xV;&9r_1#_G2A{*vIhlH z{`=6-!9(ly{l4oWYS$jR{ku_ClKBO~%HI9xQLh^v8P?#gl{|czb+5-QV&)x>l+N3X zaqg_`C%&B&Fv$QC{ypmE9~WN@?PBLwz|z^@9MRbcx1M5_+;yea!zE`S+-vYt@ZrKe zVcWky|KG#BcYq)K|Ni&?|6_;(deQ$B7B{1jrihyyOrb@6KP7G=53PkNgUN*T7oXn@ zm>=A~cM%;xig~sQzIP0S=VAX(gU;-qbb>D!Yw|E$on|O*ck?+^r1x`f({VuU*1_nQUy8$p=+Q>zi<*u=4YiXAKeB9Hb@PTj58Lo` zsEiZ<^!NYw>g}krvE%u5R!sr>S>1B4y4gA_mcj04kxSnkqgER~E-%aS-ExM`Rv8U7 z9&bF9T-GAtil$>tuh1@^V$U*Is4}XZTeX}#3h~~9t|Th`^ggH*Ivh}^G3oEa!K2k} zGF{)BbN2{*o(hA5e-Zx>7N5%+X_67GNI)00S!!Fo{4)X&T|m&cT_lFCfM)}!)F|)k z0{EE!C;Yx_kjsz5D$G5^_g}1`hl1(nSHsSK6*1slM3`w+=5FD7NkSEFen)}h0F)-| zLy_kJU576bFofaz_p`u}0c3~)iRwG| zZh7rh?6p;|eD-Q@^i#9py}T%(;`zTNbkE?TCkuW0Gqqwze_;6IG1V2xIzI1xaZzE8 zm~WAGZIPVZKtx0e$kbjw?3XFEPm~LQLZJVYAjqw0!6DNpG(d!<^{_?-#)p6%{Q)21A2FK)4KQCp89fk&VxW_I0@QM)5-x`QKp1paa%$n}0<4R`SpX#HqOP-Ffx21aiwYXi zboR_@pAw~z&$KbWfc04>4Pf&^(8i976>~&=uK<~Q5~&dp7so-7tq7$-K+rjRLucBS zi6NYw7y)KOniU4^k`rH(IlcnJ3Q?MX)yC+PVerzXrl!5$i7NG)lY5Nag!ByjUWtMV zT9khEV1*vA*!QN&cYsLO?eFZ~&d#-QA70(gui-aiA}*WnPPZo$K@x4YAo0_u%9Vbi zwF9QQUSru009O9s<`&Q!2`MNThOoz+H7oT+oz_a&hSrBtz?hVd!-158$?yIaA7K2# zUTf2zN;OT4Bi(nV_=-Fn)nAfhue)+@Hi?=1u?aWK(HEr!?@k-KBXx4OxLBMM_SNzu{1)u$trD zK+1z?k^mvyvvy-JnRVS!HDV(UP4e>>kNrj8ab;z*!4x)~`rVao*(MjiNrrTfz=bB; z4WJRn2Kh?I$HLxcfGDh?rA79;!+q)YWWjwYgaFp-U<7S2bmhkdm^|d~X(v@8fO`9za(bACoF( ziA?|lA^)#%OG+h%D69E6W0Z;)HtsFWP@*pl=lgC`W+=-c0_z}FyV;}*TPPd;g zat3Z#jHNYOqy8SP5A7{A`1cH|oAgCTDloU1O~e8V>x=8lw3o?Xy?wQv@aov{%~IR| z9~CHee2XM+pD-Hx>9%N1oWzM={Wu^ax-8lfs@Fo~kZ^$RY7gkztydo5xV` z{PS}=Jal{}VEfPgszjMO1c4zg)(NzxH+&(#qG?)-)zFxCRRr$NWT;i{B2wkiO#@ks z^d`_2?qA$9FMJ~X2588KIZ7fvYs`ore|JhPXXEMn>g^V0fj1eb-$5e)Bsc{Cp1!&o ztnmRlZ4j3LGo3Xt)IZ_V2-SGn9?mU^8?1eVpz8U9@ZN8GMoYkAH z;+#lQ4t~4c)DDSz^-`2#nh=2WkVsj82+06(hW_7qs?A*gcFDIX?EXcPt@qcC5>OXP zP%fS9qfp}C8?4r?EXK0_nJWU?840`Ynv2kLg>x_bgP6*;bzi8l1-YmX?#~ zXn`e)BcD>HQHn430ch5!@>1mnYzPbnNZMI*17aFG-?4J~QX*bCUJL^T1|@JGHJkf` z(~W_Ff#bD$+QHf0SC6BD(cH zJ%w*041Bn%Ro0zS*meDm%iaAh4vUSt7gdPT8D8hzWL2>ZIa2gFA&bOfupzyv8=g}b z5h}|v0@qLodfe;0#Q=lS4N+W4D73TIVyxKWbIMCqeooE+%wUx}NpZSR$#kS2m{kGa zs8Zn4eAt8c&lMnl7eI5DN1q-8fZl2y+5F`T*l7U{t>B~UW2Zm5xNxme_Wl&K=fII5 zCY{&em1$^RwM6^As^LoYY9GJ}6*C3X0O2MjrILBDGdOHppe!psEe-IE!CG8Cfb^6L zSk8L@c5w|`Ed|f+LLRDeNvqa4m~F_eq0f}tlH4z0M0TPM6y1pf46lINeXp$z{N&T~ zEMfmVy}cbE!ky+(Ax32C>IzKC z%1ImPqNM6{x~{7m{~t}~9Z&WD{{I(|W0a$;B)d|!WXp((jO@L4Wbd7o5R#b?LLIXA zmc2vBt`M^K=6892zQ6NVw>nP8Ij`6Ac|EV|@wh+U-#=MBqLoh-82o5BS!CK}JyjEA znz%XFS$j5Rr|-M;HsCh*y?c1uYx5K)gG>XEU9Xy>o+{#51_MpjF7mL5;hRJx1xJTQ z;m*3Ac9r}QJsllMf-gK0xij^B7Nw+>=y2X=9gJBwP2KQV8QV#X&v&iV2~Ed@5+Ixs z0Zh_)Y8grv3rSF|iMX!nbWT;QMnop3~!$rN2GeFUCt= zv(ntEE(yMrs#)h1l=Zwy0+1qlr82R1hq|h_=D5jZhQBBufg+4OySXTr@7jAe$2C8brwbjON0* zbCFD{tvjJLEPR6cC~odFj%F$%T+>*P0d_TPn&=>qFx21h)K z={17wN3s%t#+jNg-@E%u&yPc*yT+EBpE{yH;{kUPuI4b>AJteR+!pgi8Q@h~47XZG zR<$bmC=-tE|8`>Xp@(JTg_yPs-;7OM!27U zK>KKUBd9xk2EW{oGFIH5yuIUAVH@RRKP;XrwD&xmKo>$dO_l3vv`8K?TSQ*MK9=n?2q+p{3*-5MzZka{D5u`uHgU8zY_8s~Makp^J*l)=GV%L+caavS@`b=;6 zhrElWf1-%TLURZSZL+XC=FyvfYP3t>_Zu&@HQl+FEMQMBAW#X?1_}V}AOTa9HP#26?D=)y zik9cP)c^5(HY86Vq=<%5(&w-(4>NCNk-kR!yQN6a(S2My@?k3W9AM#)cc{k9V4xo$OM5{JGj3K7=6HVtApsR`1o^ z?$xfb`&`2jF-zroJX+7F}PAdbkztLSqk&CaD-7dYp z$n?eHbF8B5dkV@1(g}aelzB@VB{UXMtOi(g#F{zZ?x)c@5E36P1McCY7B(W--q!~% zD>^#Ab1C6kxk_{_s4cNrpG|7)Lj6xAN*!KEw+TEo3U)oGSs}XEV_)+T+>6%Jg;$Mh z!M0E(>!hnbQK0fg2?8gqhV#73>;vZC^J>+aou^sCQMCYsQM4AOtcG|DJF&x~ ztV`M-6dv0MMf&r zR_&NbCkn1~!MWaMQpntV?urEVg=5ChprL&IGdN&7vM#Ny?KMS)Wu!A${m6CM>AWA` zHMe31nbJ+af4QIjX=#3w#Ou6z{M_JNC2Lb&0yiEQ)tp8*!PP*efK%QzRI6QHG}lRo zv$s5zloao{yzqLw+!hZH7YpIhb*$8(ToM--hm(bN`3t_JmX&cA`28}F&xP1i3eB(} zO|nmY18$K?qkj;b6GL9V7H~PPXj^&9gfI5JOm2w;dj8wR8=xVvTW|OcHp9qcJDe^C zsVsqWdA%++su8bgF)>n9lW#@GPqb{zm;_^#oke%)>I5&40WbGMui^tiLA2|QroXcVD zx_I%x^6o|SxlVKh&>A1u&7VB^nDr0J*?OOyiwGTaGwo9Ico>PXH7`Coy}2vCFSR-$ zI=ZvSTCCuPRXx86KD`+&JqO3xa^e=OU1sLI}pBU;A zzP-SNTFws1!$SB8pP9djgXXq6^_kDLp4g!!+m~+e+BCjh6LdZOmPr_2T;$;8?d`p6 zraD}E$);OPT(yLdf+I!Llm6x+y96`mPhtv_#H$g;BYE1sf0q;GIh;eoB2`U$_MK$a zPHi0ioxiqT{TqDz%lHV+07|h_*@TrokYd#Mu|kS~V;LZt@H79AYO+{QtCGsS1(R=K zriXr80?5S#Z{IGLOHykTe~CH7o2})nZ+d$1jCm_!S38C|+0Sym_y$2Rik$aC&HqPr z@o`W%J^B`>XZ<>nl`!XA5m>ZD$o&S-lGYG#m#;4*S_$V|51x1_v#FV!uTu1ta5<$?$Ps;zQ)B$?{kw%l-RbeNGNBm^c<~aMA=eyav}4%yAM5>FQjjp%i|F6V%++ zvL);bZbKL>mb%rs+dV0mw8>XVgXy*kG&UfDl9+~n*+~$jb)zMgSEYst&ScLSHS81j z&PIm1{a%fIqT7wqKl8&OBD9(PQus8V^g*K3vn`9Q>r&gSrmme-M`yflvIPxW(8cS~ zw@7*St{D7}&xcrqHYb7=&sAJKJU++GAk(XVu)Y(ozp~KzPS%Wzjm`PT0@rokyuPk3 zJOn!9WzUEHWsuNb#C`RIrsJR0SedE$D+;0QrSW`_y&r8ay<2N9*UH^l8agwZs(DIr z)pKvn`)K$@AWSdXL-rnnH*0imTjN9)0vc@3jE;6+jQg%P9*KBt*TlHJMqi<(o~X6j zbDh2+gFZgsg+4Z_w{xBHrAe%en=INnfiq($~TRQwj|-og2Ut2TlNF*VJmf!EY1iZ?yIRejg#_VHe*MCv@cZtmB&mBl((#oc zP$TZGX^^2t#KX{Z_kb~7o5(DTj}I^C6Z10g80hh_(%Y}ixapJPT$Z4d#?2486=6Up zeODrC9$3(@eR;p&6`#E+{SqB&cG*fy@rS=77)CC#c2Vj!^`^>E3(xjI-H+L`UMcw` z3t55OuS0XuDBP4m=MPEoEbIP}ky5J8Gt!Nm%+N<3bP}!RkbI&@8fPj%^5x2~b27SN zi@FKhui+JK_-^r0?bfP6dGW8VJMa1Z2vly15u7#>AcBJ$Z;m_@bEcXi+KCw7k_mmmaH_ zb;tYF_|DQF9=Z7W&9SI;Ckyc{$`25Wdi8M$BMncE z1%WUDK>>%mt24Cd^_fV~JlJ{PGzR4B)~kJ$Ddl@bG+Jmp-zsAJIfDL zS5;a1)jnUr4%8A#u{2V?2JKhqfe4#TfcZbzm-9#B5?5kS&WMfE>cGL+j#a*kM=9jV z2naytf;XQ>_rk=7322I}DyoF_lsLAnV&xN~h=@L{9|Z1K?02bOX^Xf%lBJuZE95ei ztEub2Qp7;)byAu-a(cWO#!+rL*}7uSrc+_@bKNjB7w83?kz4cw7bOk8U$^zzn<;10 zF0nlLMhNYutezxao#UCP@@btyr^+`QufyIP&Azh-Mkj#JO55x4+ny>3y_QdNc~{BQyB) zIdp0`eNT_|ygf9%<9KbhbMqj8v9JF7Auh^r`hho#DhAZETRq1I#;xc3?c$jh?c<1} z#(cWl1+)?S7RSvSeY{?~8E+YJ^>pA|RI)gDF4F2wl*{z(94tc_S{HO^jif_o}gZ;U%pu1d_3VQQ2ul`D^NQ68=-%H#7NTEZ{dv)7*@PZ|@F4?z@30n$@Bn|;%n z>IV+;)6P`aHe+k?n?Q}PnZ3L{%F&gH^pAL4_?214ecM^ z)%wIA{U7y-DA#CogI!*SJwDj^9r4*odg^el_F%DR zU!{H@7Zs-5;#4As_iI*P!c>~~tBUwi3o_e-A`VH>cxpPKT%(s_J1up%%~ zzh@hM9ve>iv=kIDyGV^+f;+EWrZ18A1@x^3v*m5~jI2IhXz=Tdz3aL-Yzj2CJ|4GV zLqk2ADY#)Qx2-cGlE<*fkju?&H=W!Hn*h`RC4{QebX$WpNhHmK2adbl@PTpY*6`3Z zUq&tL8+m52Xw{h5XU7ZO_f7jEl=^^^Ss@7fBlB}RH!+aEW&?8Fad_FNMU;>T6*c#Y z?;V`BjVR*1>K)o5k~cOspf*&x`L7q-SMrYGb^=9hVTe?8NcgLuJ-^3u;vxwiDzgq! zDUuo^8z&ugQddj}Y0rmq{|pTcGama=(^6!&SN*!&^uM? z+y^t6a&^|dHpaOekZ2Yi{Pf2T0H91}7ys?1#-ve)xz0P7We0&;-P@Bit~xpsesvc} zCcgAGrH&=~bjN3w{^dijxC>^Dd~yF^`)Bf7ZCaV-7tz+j{h@RmxDAfYrOi^)hJiGS$OTWy<-G*5k*gwf{z9%RUfmh*B@ULP6gs|?S&kQh zH;jTq@9=nwLpb`?%qZTVG$4@nL&y16PR&lef(;>=l#Kd{n0D%E?AX{l)Sj;_`DFgJ ztH#!)T9C{Elpl+j=WgrUuObeZDE!f(>8Lcm;d$bOGul$4dd%k=po>0}BlatN92gw9 znfq(Q4dv6G_q%kClW4}p9qi120%l}ncr6pIf|VYzY+6YdRRVk{%gV8n9WXNTvg6?WfD|c@7dp1Vgi~oX3x>Mq z4>MD(Zt{G~SB8}NMd1F)$c;`vUc%Zipy-5}F@Colb^E1*C?w-=dNyN9NWAtVy(Q@YpBB+-_bN1VIt ztpP!%1lJSj{fP02aH9+p8Ez74#NpyBXy73W8Y9j(#J8VK7h=93*s6*d3>k6kdWT>> zTAQrj+g=>hmp%1I4}7wJw>Ve_;A1-h(_CYTC$E2Ad5ML~<^Uo^qkliXD3>~@=0-Ev z3!xLbsdaCkiYf=N8Wu zCKceof>^!!`?Fu^170E6HJ5d;@mYB;F=mk)vnEs+w?*u&j?BZM=H^wI*4EZIc2z-9 z(KBe2Mv`YE^pYdsb$AApv2+YtLJSEkc(g+WF3BNM*_K{ zf0V9}UE~J9h^X)H@#l z!H$AM(zGc@866ut=THqW6Uld|bz7AX6dh61r|itG=;wi*l)5yw6E!a>XU+F6Ap2up zFL<@hT+Heir_G8ncX_)xHAgiOzxMVg4G4mCHLfp2y|mq4n$Fj^v6-qQG>5FE&8@0b z&;&f#oFT>$U*FkLy}tt|(%8GS#5@R5hJZ3Bu|l2Fr>6K0b)9mL@rnHTGMiD4;oL;% zgCG|g0$!e6gtc~6G0rDL{w!dQ1H?^I8r9-&#&({!+pw{TIL`#j&D@Si<$lo#mxO+v zk|n^YQ)Yk?9YVl$3o(E&1bYnT`TPE168ZOn2Y)idip{%MM{|T4(D-x*X8+1o#!)^R z9p*%6;$@d=ZHFB=8_T_llf;U;fJ|3gA;Jd)-k%gx*cac3+hl);LJOH~w}BCAw9Ffh z0*sGp%nP+dY&xD(l-rC6fcY8XOTG+t1?a7DJpC6i#R0k`fZ7tZ*YcBZMs{KOp*usb zP}6*`GPQ~yzi-+3sbtnk+poo%gq64dpZQp^Z`gy=zl7%OYZImS z_?ast4hS^p=?xpnI@MtcKb$3(q;VDa8f*;=0x{6;4E5KjR=d%pROh@Dq1rWU^2vme zT;uET#lGRC!Rp8cd}tm?;`#@DRjhqvqqhp59TxIl#t9d>NIl_cGPR6L(Wm#f1+P<0 zEGp)MBnVZy?nww@&K~X zSkvJCL1`eFNwj+UUY~FP|L5mah{4R?HI8^Ld13q8vF>& zuhb`Qtu)p?=1<8KKw`3=;n@jfMrZzMjkum16;%K&ArL^Ao0@5zG^eWPrgHu!jPDxy zsyzY{@~)ykD>(2CLW!b}KD$8AbS6pc_nTf-cx)-Z9xPYqmYv$;P8RQ@?K`SPC5+Sd z%gu#QzJh3>?x?rgd1=T&fa&Er7`63^oHjdQv0VcyLY$f{kpJT5gI$Dy6(IIm6? z*&3Fs7?>8akz`1@d#dyWMIUEMnA8VK_`N;adoFOYbOrFJ{p~xJt>KA$Hd6~jwd$1w zro!x_%Z$4#gRXEAP4zy#7FYo9Sw7qGZ>}aNC9HQOav+x4eUmIK(()j5&apmmsP<_- z2_tlj3a15p!2&-Tih+jNjUq97D}eTFH${rdo;YAyj4TH+Z}P0Fcb94Io5eH`j+fi` zhTxL~K%pGsUjd_D88VjGJIepQrG)9rC#57MvAl2oHl|}>Ku2!^{+5?7v05^WuUz@V zbxZ1f=^f1Gu#m00a0+OOHoUg}QqBPCYh}7#lz}1L>5CHPDF^x0(OS<);>Qt`$HTw& zrad`$d3Rwcg&hbwvRk&7+>_>_EJ$F2effp}k9+4V{lkk_Z{&^_tU{jIR`C&8ickRM z@Moi5r2HZ1{rbT>#-?4!%EOcUcyHCe0ZJpY?Vnt5>R1}dGv^=UjNaq{L3r1lmX;Q; z&5i`FjJ6y6PIk)`3M?wj5FO% z8~qQOUe%XdZe_7gRv&73k*X?^-V|m%t~Fe%<@2goR=gIhX!6x<8X#U%F#|#d9WAfpMZ_j$TrG(q0JoioCiR6+rYmG=8-2lyPdSrPrb7Y14i^cy@!`I zSA@#S$5+6Wzx=Zh(s+Qln7>qIPgoT$RI*lJ(^PeWw`cpartI;^6d`$!<9tU@SGjc2 z6$*-bR;{@J-W>NeI4k(hDe=VPk%xrNw^@!`uO0_iCSSR=-$Ne&A#Q)WlLyOmg{mjv z`dYHuyRo(LlSZVfGP%g$&lL6MUsw_5z3ROgmo{H1MJz$N`!o z(A=y5)dZx<(1`Qf8Jx-IyKM8%eVfDiPt1Gp)M3a+J$0qN_^Msn`gMj-;Z~FRyyVA` zH+tIJ4JdC33LcII_r>Kud$emY6|Q&gPc|{1JBNq@To3VwV)2S^m~P&* z9WA=a&YK7K?fWc7fcfc``@s31qr|PzsLG%9digfBN|{dy7|Jc1hIv=U zcs%n)-|ytGr`DGlk3cdAV0cX4waU4rD4PsDx!f9LheFtt}nW}fq8#)ILbJrgX zM$KqQM_&tWq}&n)|N40!Kb3#gp%NP#j;vqxzc{!{4Lc=^xvjAmZ9$3ee6VR6XFBs| zhKkq1VsFd7ItEYl88#9?*Hi%D9|50Z{=fq0li&ZRGFj(7GL)m9Br?Lvz^$VU0XWQ? zi;IbE3e@%9^Dd*@rl2ACa`!Nx0foCm{-MWxtCX>9J_ajZByr zFX9V({+V5bytcX7S!iB^awA)2F0?53iV>6fmZfDPG$rI^WC#c^S&f&mwK;%62?E=C zlJ38Pc!sfJ*Yi$Uiq)YGB+F#|3q&iDl1c#{xDOm5Ji1{Kp*~rS_elEFXl~0PAB9&I z1bGt!YmRF+X6ivyF&ourTl2jtv48xi?`ZebRR~M@TsgicN=Gw^zyIYKLUaS6XV&8P zLmKu5uB!ZUq42IV3fGLAW2=|kXJ*$OEb+WVh}Gv94xTrfY%PQE;AaIzt#5;``k@WD z9sO`6vDo9=V27{q(H`G@k;G?JXEdrLfp%OQy(3i0fE&cw3YZLH1m}tT(C6*t*aG?* zSB{Is6YVo=e+=aY5v18tljzP9G@A zGVk1LL6gq>V^;y|EaqE;KQsU44QXlVxw|pD6{k+%B|I0&f{gtl{VvzZVyYOHLF1qj z`z35d=RoQAqky1scJheH5$DO+9CDw`XEB7&NTv1`g7ldJD;#cOqq+*FnkLvQ?R?2~ zM{c$Nk_RmzpN%K;U1&c*N|a2h@rus^PR$nIYRge)fG!!WBL#6-EH)w*|B!pr;|KCLbJzV4l8AjH%!8g5Gth`NP3A~fNpxC(Is>j{i+n0}oJ z6{&AGQ(h(V^76u{n|&Y$*E%YfvBnCVD)gkJ3Sp7WQqaTcf`{Y3Atrrt$< zjhOE%HqpRe9MdOgTKxI$E!}{#PIQ5e=O5Mg$vCJ}8s4>FI8kY$DvvS=5a#Hb^GJr6 z>8l41QNPf_KdNwi#CrEAD6wBQ(1&xSovNLGtGPO%bdtXE6L(aR|F*ies}>#!IaJr3 zCFs*z&i`s|E#Q9_0G6-Bvztip1wYa+7P99x5eE66OM%e%CBZk}&)?5KU4?5UQ#a3x z-v3t1|1|mk`!lu$>U%0aee=RM!jzBgIQy|XB9;$R+gNd5Sf9sHoL^yNe)W2Xc+_o$ z*fEp;W>}g?{2^Z@{kHmrZZ#}UB+BzF4O>LQ*;uzN_}_+plpl#idTh1AyKe~cQ3fQd zZ5#;wEHPUQ!J2o3%Eo!f7`)`c+8qS$6Jwj{D(}PYM5uq)sUz`y)ts@mzO`(TJ3t0-9A5Luy(0obReQYL$wsI#a;r{#3`S>HK@=Ay6IA{Aa0tyckD+Q z-<_>@NqE0bdmQh-{@pY^kG)j!&qtF z$H%)aLilj(w+rNzhXL`N5qvyXZ7F_#JKL^P(s;$?Z%5GeJ+cbdR#xS=0O^A{#S0Hq zu*6UT$$1b)iT0-tq`aCkucSN(y7beX2>}wrw9eY`4f*l?rUVU00`HpRj++#sT{Db) zukls^$C!wo0ZbIdMaAdO|JCzgpX7hv0Q_RA1aJgKS5$W_s^{zG=+{pHS;1-;qqVP_ zGJlPx-A1mU;JqZ60W^oN*Vycm_+93CQ^24iqA^Hg)atYJl@Fgj7yq=vViW{_w|oWk z;<5Z8_Xa^&+wlDXjBWICxgW;#Z=E3XiM+kXKd6P^JCAOO!+S|UbfG09?0lXTN46&1^8mZwFK^nF5cjV;$z7Xn&JYs6 z<$;>$!VV^b<1Rl*yy#EDkBlCnzF^3&UE^8|>pN%fgdkW324~_>;(bwu^zW3hIvn+1PHj2lp5_F#CN=Ht2yp$1y~x#YFVx>!}c zbRinSWH@P20seEcPqqvYdKQVbgzeih|GPUjZIuzu)?ECRXh15FzGS@X9>?c5CsB&JTA|?HJ zSIQZYf`pLrkaUm zE>nL%V{RM{WU}F6^S!7j1LW8qH;qeQ{Eo!HkTwCIG;odPkR;bZztYEr9orZWpP3i z?UrwhgCHma5E*u+3p@pEbp#!&W|2v`jV>!Wq5rzg^M8;tyS3TH@l~U8vvnt_u~8f% zxkKN)d25Qx6(YWny%zssx-h`-=kNQNZ?CcIR^cF@H3}t0@^oQB zIr#bV|GB@gPhXgd0e1oy!shaHz8HNevL_ESoqljSevX;xhLVU5Q!BcoP6~~S`KILtp z(LpDuzMa=bR{*u3oGmwxOO9?R?EciD49m!1o%c9k)H2OK)!%c{khfv)DnvIdjo7m8 z?`nh2DO;a7d}DoYD)rv{5KJatFB?Gj&x(gm)7K?m9lbjOQxV3!NW60_6%gSrNw|g>PW_xDW zd;}9`0NT6eV87O3UwNH^sO0G*)(DygpugGn_`Gb=?F$ z#{QT55g3)u3~0vni33O7YGC55F%EcwN_8@o z{QUk6VQR-Jm#Rvydu*@D^7v7z)VRq;*6AWh=AWmv6JH$Rl1{Z9!a;u7RmE}WuR6$( z(<-7R%g1+)w|cZoYzTOs6+bFnojhXIuiH#E3@F5opcc`}cKK?a#0$Y&Uv*X>uGA*d zdW#Qq65?@l(pN<(asjP;m-~5pl5MhxXPv{)CR!rUAbG5`YI_+6^;xA(DL!u!gkaxi zE6qvg&SLIylMA2N{DsT#aBodL-Goa$ubUG>3EX3LZ;U~HL|i#1;@9B!vER5Aocw^P zJ!J^G*RT?!UE+4y!}lt|8}}TB@aF`N2nb2|mx#F~ic`R`d@wABg%olXPFC&Jxa?@n z{UaNQzNdax^B2yN+NHi=y^vqg@;)NkesTqYzM7S~ZLRlwuY#zTE%7E6!qnWdw>&xj z{ddNtlrJ+boj>sZ?>60BQ-nn~K2z`AY|O&OWC3MU z^3fwGkQHZFd|V=Cz*Kbn&>E~+O%e}hjEqGf`M~ml&tJIv^lF?)U^958TB_0!9f($_ zlR=@tg+xp^I3Z}1He}S8@EJo8^_f`)s8XdERnmCKeP(M=s@2+o@#XU?h8lnY>{`_C zdD7SZSu0Zp$kUkN+=UKx1PhjWX7#+>Urjd>Pu->pG+E~*nn)AEf*%;wt@XDV!FIkIe=>_QpN^k+Q;*UF8fds#=y**8ZBbD1 zU>^FW15`JgoYwPm;~WXz;7&T!yq;2HXLC??1e$6@J{0iS)=*L!(6+75v4lCSySqEy zWtwmnO=>H#wyF*$qi_;`G&U-Y4)sV_u&d}^cF$y~r$VO8+*k4>y7l@&m2h-b{JfUI zV|6kqo=68yXe#cKJmYXK5IGavJ#$-{wWBrbn@rWw*c0`S=Le-*@{)vzH{I?Q?HswtAvZr%^{Lks&%%);iX z^7&bB+rK74uf}AFUu=(Nb)`8l7;dO!QBnS}v$&1dgEB4OpX2!HaOeYZ&@bo7r0(cE zd<|!bVzF;cda76e9!octUFC>=q#h3CC_(uXe>4NCKUJAx!&VJ(yXSel@a4ykABDz` zLGjvAWL=mz{B^X#yDfr3-`aMQ1p5LuMCJl5hVl_-G3-?x9d}^6!m*jCC^TEC0%&a9 zSB)!3R9sA@Lu&ayt=|&?G3!Sk!kQa+}!SB>WkP3^s&D6x?Q+769 zFi1-!(7J#Mu_-Ucj$zVu{t}Cc%~APEjTm$t-q4qQuT!r#`BSOop??5K|1Q!SY09I2 zPD~t6eE6`i@VRaDJAuqyd?G@o$it7tEq@q4h{o>2EGOc+5HDSGUR43JL$DKj!p}(| z;#7Wnhu_~0P8;hGzow%INKYncJnD?a1_iUH%@U-YacEJJ-@rjC>;&xr5){cIh68L= z9-ihohs?~ZASv7&DtM!yl5Y6-N(^X5!(X-OUsAVm5h;Qwz2+Znf?W`U2}K`1^y)w}fV=0LKiE9h(5SRO_hTscp_kE@8Lr-RDHS?E(vd|qD? z-!%WSeI4s1(#Sh%5TwVE-S^~auNdn@jm=SiMjNXcA-!b0-MiK|5L5?>pUA>nT?66Q ziU9+xo?rH_@jQh3G*N-mZl1fs=kf%<@3b!s@vSJTBRGrAsed%iwRpe#idWwB?9oZ^ z%+KYLV<<9rR)@k2{xbuIN_=CG0EbZ}V5r}hV$^aqeAa*Q)t&5)J74D&L#5hIv9bZ? z*YsmRdwa-(T2Jqj9X?<^U%^cSYr*UIE2^qT5B;<0a5V7D-YtSW37mLF4Ssx*ilELv zM++Ct$yDn`w>-aPlg`gl2QozF>dIx|$Ze8&_hdMFD1COu36)2>n`0oaeI?z!kZdlr?S z4=J-$;5Yt8>6ssl#-?J{tulcaj8=sNN>b7V;~h}WjR7mP<3e)MT9_##vgEcPWDCl$ z;t~DpMq4|e5`(;{9#Bc6C5m!EdE^D~Vc4Dc0T7k96d0vQ8%ab&w6EGR0pG+Ie?F{6 zB8}At#K_RX`B%f2gOz|zIWd-W4H2myqYS_|(6D1vc%$o)x!%F*|Ne2(G;_N7ooeOb zpJ(NR4^gI?Fu=M@L}W6mFQXxW^WicuzF{A4rA(6bdal))JT46cJCe6)(yptJ_E2xc{8Cr-TK}wXjOe^aE$&$cX6>QB{9*md>mJt(}w9DOBciz_uP6pKjm)j zR{C!UW`^lPYY!H$F)X-LLLk5!m0$kIEflI1c3xoHA`M&1$wD31t|i*r7s5qp^ZYps zbDB7-^32W5Mpx<{EwQJVzf*=BUFE0E%|ETnK7E?~^$T)?R8)I9doQo=&GYxRv%`8+ zGHRVJ-AwftSAK9CEbZv}{Gum&1_(9`i{e8%Q9@FypgQb~M{ecoHUehZI?%yxKZ>OWklx|&$xi9izb@9R4=c&H%yiD!~z2y<<+0I`pVGf z8ab&<=KVnRwHQdI;91eT!0L}CmAWkU@ooiOCkiImZAA%Dp1K{YLS2-~ai+zpKWiP& zV!jOi^%l8J*U*Q06Fa9b=@`z{#OdFFjgU%>@O)i|ip5?S8wZpPG2I*$JZso^&{p7B95{JJBYw}Ob3 z$#N~pr3oDea!r?TuCAh*24S~iQUX=v{{SMkE;siAef%D?KrjF; zl5xkj|D(O!)?e}K4AqLir!1)EMC#VHgGGOD5O;lRBJX`Mw4*QBdqrZk9tU=?$xxC` zeT}=SDy4Q`QoosBGsZ>=%zb;n;GHQ*d)*e+U5+z{|*|DitXK-(Vv zw8u9q5vOjvyMf>E!cK)xbFr^!4Z{$yGv>}$=6@L&N2=T39?LFEkKh3q>v(xrJU|bk zg%rgP6pAE?b^C7X?B3Rp-u_(vD^+=sLY2a^uu=1d7VD(1@t>Ct;oOA8{Xqks&B7 zx`uBIqA9ou-*1zicV(}wlq{_N*RNR*Io3NC`he8;Ul8LgAY|_B#Fn)nB{_eR^xglF zT8LE<6#Y8K4zw>xzHJuB_~9EFZO=gzF;ue;32XbM&d-&dj7Pbr5uX&c$7huAEv}i~ zL>nE_pFidQy0r@K&BtYfl&IGisUB;{?1xD0IesS=2Zg+3@>Pp?3Q2xTgf}gqv*2yJJKNYfs@P$E2;&cO%t?-D5h~Ddg1DU=z z&L8W<+E~E#z$rj^w7E6!UsIJHQqOUAa&l_8&V9B-fV8V~>dp9$?ffy!_dc{LIsT`M zd%217%)LdsWLNjNMb+r^kmcXP!to*jKm@NEnDkG)e~A)*vh5@$2oGB%IDK2%4nLnq zst~ZiovG{WwiI&p6*+zg0mN{}Wi4WVV{WCSS6RrLzBzUU8v&w?VFGqh-Eg9WYc0Q6 zuwnSE2;))FB~2sBjJJ*_+0Kjkm97X9@N$M<%{SWF>Gb7y-&qp^Y$=8z|4Ub`aJ*bN zU;eT5z9#ng@#A^o+L${st{5CJV4)?b70gY6G?|p0o!!w)=%0%pPbyVN9s4%r1wh?u zYQk^HlbXmC4F&q3=;@m+W^u&j-8P#|cs$&zpQfi{C?%etIH?3)Jp{cuSE6i6ikh^N zD%n}@>)V0r)L~qZ*pto-?5yj?3|&PoiUBW`Nk7EIG;@ZRms8M-MKisipl>D{6NhwM zLGahz5g2U(wje~JQF zSlD(oy+^yeGu@<6YuYn^Y5dZ4nIhT2n5M-KBA$z@BA%w2F87jz>ITzH!^i^-P-*>d zlqDISa+@0v(>{0rR20ZJ3Qa$!^#8^a(TGze?cVCHs|WzALw9?-3FYBR`E_UC{OeuZ z?>*L&2H4uTpiE2=e9;P)dpOK>#U0GPd*D-77tORK?0R~fIqLX#qW%n$*8a4F?X7Gn zLI?|!40Rs^0jk$rP}aqzcY$jfK(b3;1)YKW7t=()B@CeFs-{4u=nxAE=o~k9gpAd=`@{u zjH(>w+oa8sKGW13-TK=%U1|kVc2kHPYT*oDa=LLN*%$lAXOfIFw>-gI|M!iplnOq3 z0TWu{t(`s>r5im|{NT2^ygw-)+kHLul>Q``N=Yxf&Q?Ku!kJtAhr=~6f*Ku`|Vy%a&WEy+{5mfg9 zaHEv!z56;ffBH|$Q!=SMrOE-@yD&j{pEWReFZlBHH0I8Rm-=cL4a=_K7xC=+?m6-= zrWz(|uZo&1*hS2U9&NvaG>03aK1OZ5xJ78%0=T!rR-crnc7D$OzIZ3J>e?zXzg&GZaz4u$`*uBJrT4AW6l7&X!^WozjoXMH_z<^$^or#c5D-}6v$h`2)ig8v`?=q^ zj%wZ1&8hw0{Uu=$k@{1QijGeBOb5)mG^?DXl7_#ANe1U?RzF~7wrl?-ev3svOQIup zITz|@KHFhde*P!Xv+5Hlm^3MQ?zLuq`XrmmTdL9VL@t=-fzj;itJzdM?pFE}KALfl zeMHiq^rI26Cr>P<>r&4_S{XjH)?+1eAfF`s+XvpFi;S+jKU&~AKi!@o2DW9%+*paN z0755pyV~Q;KX6F6VTiCgzH~-!dPL%3`DJ|u7pQ^)u_TtQ?V9p!1<>mk>eY^&>z3eD zKR7TbA|e8iAsFvc0_O?RuUF9k#Am(uB z*LkdeH>9;)^KhQtT;Kj79A-h4nUxiLNB3~1uGZ(k(#uFG`qW@)!S5`E@Vny%a`{W=YyK>H2&WnzO-ycke8D-6gT@Dwm>zjO2MX8X*|#VqWAiJ>+h9M&4bf!$hZ09 zhCwK5FdAn6S*wU< z4NiY1BZC=S+}_!`>GzD3fzrdnt$;NtB_)kHlu3GOY6?vfDuK|!<@i49-oW72G+job z(=$xIqwoBo5N(nXlqlqAspc~Ligs7!CK>jVCwgiONo?whF#(XYA?hQ6{t!uf zTLq(G-{5z8>z%af5cao{{)U&?G-?PP~fEq{zgywzTa;;pA?D?{9b9jNh}Ghk0I@E_HHz zLaN!X25~{rOvYqZ??13gLw6{!0Edbv*Y}_^Hr4mkbUq?EniVe7DyKW*Xp;2J0lbaw zoi2#{o8&cFr>i%xMUbBFfk-nw!9&eBR`T+Kg7wuk=TZ$;z|-G2n%o1A=h1TU1Nm$% zI?~_Em0_BiiKKEl00<|ip`yxV#*M`ZiDC|gJ17`^lS#Nxi;XduIPA@96319?`y)8H zdhLrVguax@NaxV2a)VUeN0+XHE#tHN%Gg@X_~p-Z{LHhfqmicA0tM-rDX!Srk431B7mhdjg0r(oNERofn3vv7H=>JKT`}qbr~NJ!cOu@tP*&-0p2|wsKcG>^z5D<)oxh z15T#wPhv!cp;!#?5=E?@?jFyFE+`ESU}0&rAb-Nc0j~f{tImPDHSajY*^)`Yj=bn} z`I;~@KabcIE&%hVwHQgu%95I1qsO6xRs02SQh7OFV1qSx1ogcVEy%IU)7#nFvKrW5 zDV+9JQ06f=h)!iPpEqfXVJ&l*yIVc!vp6v4INzsC8k>;t3G=V}iz7gZXI55Dy81+~ zy}5{dOlO7|m9gsl&ctH#WO>c7jGlPQS6YP*L%<^GHY=VqJo{)K+5VYRlb!PJ|D)oub12l=PDBUHa(pgk_}%XA5vnc`!KSjTlfml+A8cr zjE3~|Pp}5yfEgUbDqdv#z?OXkA`i zAIYc$da+$YHQs)+Hq559*WsX|Kfd43i?pJ37_@<&cjFn_X+uFK7^W9Yp=jzTD?Lhb zjo%P>q$)e)@ub@4jpbYLoMo-|k9UbWUBcgIIUYq$ zj&i8}3`(~va!m3eOh~zn$yedX?Cfli4A-G{7H&SEzKM%P!m4COb4{|bE*g0S<-Ig3 zTQ%dq7Fk|V9wPCZHm>9ad z4bB(myizCMEfyp#`{9$?za})@Ssr-*H_&hwLrVg+zsh%)){$8g)TvDR0eRF;M zGwt2ARhRjI^?~>6Bl#jaMUO{vR8|KDOf(XNG_Es)i~A4pxK7D~r$#Nl`j4&)e{8hu z9^Gub_&XfnzfRf#FA#HcGq9Lvqu-jo)h}P9lXBfXJE-l7mjHMnFYl&8)U{76KzXw4 zH4vgngiU#x=k)SN{avy2bJ!BFr(8Q&J&4bQMI=Dz7Z#!naR>sIH#S@V4+jcS|3g!? z=v?~!#T94(6zM$kHYCdve(06}FQ0qwnrQzMJ#oT6hJ* zxj$9vrq-J>qMC8n4JZQO_&CP*(f+6&aB+@PX*U|lt?#!O4Fs%7+lkMYRfb8!@99`< zeYgF_PB?4JRo428t%9bU4Xv816`1Y*tSPXJ^j|9x-6yRYc`h;r*RaV$GjsD2w1~sx z7xR;igzpJbjDg=XW&OETtUsV3HgFR1rZ=AWZc2YS2sqeq9RI9i@y7J+m77_;w<1_O zWt)Q`?+=V{KkIB)0dY6&^kXY4#z%uvlU0aSP?V4FDI^BLAzKUG!f=j?4|Gs6MLyai zp>_pQ84iltr6YE)qM{_-7S@h0Ux&6&vrezHxM5{;d6^wumXYC{JoEvSr={LIqd@$@ z$2}xt5abgS)VuP#iRpqR&Q!1Y_i{^!l~&;CQfXs`F>F*>l|0-8lGwEtet&wsI_f+L zQcoZmrU=_k?oII+WIn|bxgJ?Ms#wYQ>pDV)IUzn?fjJLO6Rr;OhhS=fqJtUtruhQ} z%2)SQlqX6|TN~guAfqcV_^@&^HDj(Xe1~P*$?p_|xd&^n#DTC?Zlxmr%;t9Y_PSve zlsOs&di-)ttpqVPVMY4o_?VBN`?^g;1P#dU_Vzd9`&i7otiN7`hby)bQ#}sGIwsRn zTx8TFK$zujV2b~}UNTPjww!b5S?Z#(GdWD=7MVDy21uU%3BG~rcUnG2A!qToiV7iz zkc*vPs;KB$TXO)mb3_{};r1|6FU3^d%1YJBDiQ9L+VE61wv_vY+SxuUG_B+7a+;cD zKOJCooX9%zMmc;jdGfKf>jQM^&)%^yfs3M<*B=w+^-vlatha_JsgOtnVo;DmjDLc7Ao`CQVoz)eB3S++jcs?nU-Q&SqwU zTfnwFuTc2QgNvGHZ*whEl}vSWnL>;5SC^LRT=0TEEYA?OS`t?gEJFhZZ$*gHc}j#z z11o?e?`hoEc*H$S+G#D7YkS=GWY+t-7)pcWp7}#o{jk|&a(eTJ!w>{OL!qCDbvV zC6%s_F#rMv5m*>(jRfNfFJlv%f44dvo=nXD+%HFqI?Cz^5|>;9_zhmOUz);8CrfOj ziNGWzB_1C=f$WC7W1lN|9&Ol|!1uKQ0>qz9=2Qu_wcUTb@%vLh%lNKc?&8zguct40 zsl{7qYCmU5tAA9zdglHthyl0Ye-2dq7o9UVjL(~gFB01?zOf#^Sh?*(9Y~*cA>k9i z^WRMEzkmJjefQs`H%RqgHTQo%`u}|h%LmEVsT-{h11m-FY43jThF=JNC3ZZS?t`@l z44IZLqm3T_-Zkr;`ZC=H_3slG8#56|5+n`+R}cTK36*)$R`Dw0-{;`z$X9Y~K;kv| zf4A+CE(d}J=NqBxm0%_lvOsN(bk5z6fm8oluck}xnpE0&G9Qo_a_kz0|85JMb4l4I zT=@JAV|=Yf?7zB%tvDfj)g$mb96k9+qnw9!4^;ZEEH&oanDzU8@WOLllfRwjqE;6n zIC1g9-=tMAHWg3L=-}|*uYHsuO;d&$phPiCilzQL^BSol`BjFw6#xAEL8Q*gqFe?f zNf$1&bcQ@^*;pdxKxGMB(fkO3WPm^D|h1j-zd_E0;SE2xIc!qo&1d{ls7eY zMq;*jjidAG_}}(FL@M%LKluW8f-c;30xx7LMv-#Gl6Hjxff0y=#vMF`!epz9?~5ye zDb{I$gN6}{)jd6H0w?{GwSkQ0bTmly2EU`&SQ%6^;d{_jJ+65sCV8XPhK~~XxR0yL zNLo9~PRFG2cNu$*o{Pr1UUhg&%61K=r95Zwl2|&$ve35N0}dNv`}-q>pxM|`>u{-AC*nPp0|=HhxZ>$Qi4XZ0q>HcU|-3&U0$zgOKJ zjWn}CBZBzjEdPk$%OSeUCadaD zQmM0E;UMpa99xUoA|c01_;Nh!>Sp~#>~4R$bfE3{F6~&6u`U%A^L;`f=dVnk)z>(W z=vR1kjdB|bl$Vi*K=_Hmy{Ky=(#sPaGc#)O=XUf#*M!Uj4{^G<2(dUjg1_T7T?2t6 zwXB2C6&fWAa+#^H5C0BIgBRH6Uv41%wAdLEF3yIKND|dG+z=clNbjb)ZuXkWWKRS; zoxi;xSj_eJ(zwtn)JD7p1#BvVc%-x5kSQbpn9h2Sbp;Cx)()5ALLBgNrhj02p#9h0 zZyWBO3Ab>P>ovXy=^&JHql@^Psy}=ij+uJLlYM9eb69h!Iduw}XuvYsx)wE2<(nC$5 z^S2uxQ|SSEW7xCG65hKf_dSWRpt+2zV=(A<@&tc-U9dP@j$=0z&m}e%zbAaJ*<0z1 z-@f`%)?iWXodjqdIKr&tB5*SRe>7ta^IAX|MY27}A1jjdJG6=0VzDY;7U|f#Hh#>ADhDgKr7-lG|+hTWbuOR{smpfWHr6?!bB-tzInz~PD!5PLN@9%{r#Juy9BQ~ zR2HX@aSoR9yY4GLfIUV`^=)E3+Pp(mBH*2Rp`CX0yr96>i{p*s^%y+uFEZ!H8H5Gc z-*EfZaXopkvaUDTHD9g&cUbqWb`h*>dKX^gS_hf^>U$#Iadx+FcvygyHQT9v+#^*+ z!#jf(HxB)mcRt`}jlaE@p4ao^%LO|5c?(J!DSk)9T|yT;gzs6E4j3nM+3y0E4)gKq zi#1)pH}E&O2Q8fbXFuAYOwZ!!n-3_pDETkgi*0_1T+g+wa5yRh_>m$C1)NS8f)a*g zSLYz*Y*K*yMI&jo3!O+F1bI~`X{BI{Zz-5WvXf4I@t)6T#;I}lF1CH$o<|IL;ydMc z{OkOo3Px!p+a`DG^29V=y^xI|!w&Jkso~M<9Xx$Sj*XgL%++FG0Lxq5*Db7!v=kmpi zC8rnrGiJV6(zo786L;s}1Dz^v@lMmyY5hkI;4k}wME7K>Ht_J*$;)8GbA_l?&`tqO zK0peJ7^s`V$*y0arX_iVuo=jdt!E58{R19jD19m%Ct6H(&%dRf95d3$W3>^ft|0_3 z?@m(vJMNu@d1IxBNLs(>Pa+UNbLr#_FpU(u;kGgPLcqHJ@c3|BA|o)VyGwp&i)VIg z-F0m=`YX+_pu5PqQ}-yhZ@>=$$SC_mx^b5Q6QWXGr!Saa&GxMgt-$-P@#)|xN<$+K z!)704Ltt~}UHamxXMKU|t?n`g1_oMM+6fPbdusyD?hpA|vmp6)rq;86qC0i*Fjw>C z@m%F2?Ge>%+i?p)H}Ir~{u+ z1b#UxX{!cDhF(QA9~T~knw)cci(LAhcJ9zUjDIOvFU-;wNU%XcMVlUJ0_NvS8d?0z zNV%+Ln>)!BZtQ4~Kb_PLq<@ja+ZwLp8Lz8f^FrsahL2-V5l7`$qv-gD-iV^a>9IdX zVFn0tn!c_C1x6laU>`@1DCcb=h3 zcq8;9+U`!ue*1d8=Ygq>O$l2UA&VR$y9wAs0F{`UoP6wh<==*i5Gy_#fe?+xLcj~^ zRUvG%BtG1i*Pu+vv88?x!qI)F?OsG13mU_!yrJVcDlg{t{j?(={jG$g4bjY8YvyR1 zqN)9G_FT zK*O}aab)?7khx9srJP52nkg+Bqn8NfsM!m2C^C{r<(?x~S-}>>wN>(VMEW`IzA?3o^JXG1>q|U(mIDZm^8fAgil=%_BN6oM@xe@w6$??a0G_SER-q@ z3UHBG$y%0~Y{YUeMedZntQ=(uC25t=-U?#R$3?*v0ePACg@?ynu}hM%3-VXB>%_nGc`C=$`)GI@>@u2Zbk>_!p&q4asvh6Yu*MLCfFoL1NjTIOmIABZ`v$hKNppR6TKadWX z0kDs!c;iH#3MV=zk;mW~9fN3KUkgBh{r)arC3;1)bF~K|i#YD&8ICzY_8=u55lbgu zPa^lkzszpfXi!q_wk;ZqruDIF7(E?FF{P4< zVaRKdlRwKB$rvRce|GrU5yNPYRx$7y-+t3wNNwLiO0N$+f&fXqAQdq1-mwEShytDQ@m6!IrE=mME<>~7<3di|!qblj ziql5Z6t~s{)MY-hk>d_9bG(d)jW@Z9UqSwKf2@{)DxSV@>J&N=iOyPYo z6bv8+7%S4Rb<6F834LB3Oz6+=p6jA>rkA(+dc*`?zmB)=f9HR=^b06`duLyqot!oz zlLZh6$eTRO-n=cSqDCX_KXdMn`?_k{Cx5fUpEk@z>{amMr0^p|GXx|blPz5Y92_)W zKZzQCMZ`pjVn^R`pNoV){>ym10-MrwNgp_kWwc!&0K0!a0P@p-^PL6(3De>q?!Ha@! zilpzx+Q*L{A>18cQT|6GNkCpr6Yv}bsyUq?ECWS>a&ouE#f%+?5MoEHT-h8V4eTsex6XiYnj`|Ok+l3dgANXS$TGLb{xF} z0B*sJ&X8!ZTE7Nk;=e;E$Pz9QCuNj^2df#Lv~6D#GLb0)&eL1utv8j>=*R~TAFNGh z`BvtQ=oA{kunw>aN!S6?DgO2T6Z@d%d{P_W`1+RfjZnUJWH3weQGl1S*v6*RdN5eHp9E%a8-?va?Y5k6kiZ?>4%tZ;Fyz^EiRc zAx!Z77j|D93ZR%6xj#4v;u_r>m)`fM+?Tb@}PtG}v|!@m@Ix7!OW3 zY4(Q?{vsZ0obe=LX?-vbe)o>fk;&lM9vpjtE4#~Omfy0iE!HNhcOZ1U+^WZJ2&18@ z+7}jey~k)OS933#d2OA1tDP$za?G5uv(@S_gU7ousB{e;c9dh1+8(4J&_h9)?N_u*Cpg7n5 z{ReoAVb1``}eEY@8ZIcXb2@;<;bqd+EYI@*M(6Eote-=O<*UsS;^_mQc!9fofl79v)rmvG$|Easry)f0!G~@dTJILZKpO zlx`&AJac+l-mjJ+F>)~95W@6SBG(&@gNVe>YBu@ecF+eu@BvVa+`C7uR8ps^-K-&~ zT`&e1S3;(9xagdQGb_mKu8m=knA|x_2N2ip?)Go*b4#@P^3aFX@1KBew_zx<`IN4e z@8Qtv?<%PxZ#WH$04@XrV+wkt9JrTuj6ExxcItjEwGVjjg)@bqFVUHcz1M{3NeTM@76q4Zt^WB{X+4uJTsTfA`ONE}?}_x;*6Tr=cf$VTe>jt{ z@Ca`i2x4b8gW>-w*JE`Et#;2JTc7;ww)mMI2KF#M{SDMQ9GtU*_2~nhw5qbQvIg3{ z-k$=wXiD~N)ujE5-6K{>$$EIjXyvH+Rr%%$agOySi{#3^Aih58{G-x;41VS5)&1%7 zvlD11Nm&rgySuyK_SrtznQ3&M8A2)7pGvTy%WCxD;1Ttn`+bjqv?oJcnuEh`p&;41 zFa2jF%&aAS1LzO(lXAyWD(#(S8d`VOx`%xJF;GYRgbA5Rdcj=KF%w~21%Ki<`6A9g zwjf{xuq~?Tcl2F3C{fa7#GEjQJ~P=F<*o>B@u?e- zw(3ose_M53DBjY$H^tqch_79uAGmc8vr0`y20NZS^Hbf%%|ZK&+Yw+cKo05t zCx|$w`)eFniyg%8G*cwmU6wyu&_c{l1F!Br#=CwWy~`KKHGYLKF@-V(g=Ut{L{C*a zT1N=0h~YSTh*UaHOixWo^0RBWTnS|(WXAgX>zNF89gc+c!_?A%-s8+nne;^nk;eip zEFK>I5rNl489h{k`&(Nyf=|_-dNGB5^m5p3!ouNihke4REAr36g3hN$f3l<)@d;jd z`51H!GpQv-6twnLx7-@4uDa`NoC&HafPvQM4KI7 ziqu7!any>CgP8Ev&>Bl#v&qtrB6A;u<3vY8B%XdWdVZ8fY$%+cW`)3}> zA*a>vrIt^Ws=rx62Rt&ht+5%?(%K3Klk3R>{dChMjLDtkGkc6 z^e4Xfq%$$@ zrxkac3o-mlva!Hw-O@=LCm^NMtG2t`F95-72472<2nDQrArcKRqWTXX5@{q{%o!dL z3K*jKIS^hydAt?5p`8enENusVJH%!tN*L&yb~ZYNd@%zEvUImOA+cg{H54 z@(4@rSFq@WB4sHFOIUB-{=l01l17VDqQ{=#wOXI8^fmwl=pn^9~$8Tb}F9emKX5h}x1X5cHL#YapwxLw!yXPnyf2vAF<3o`|e1C*a`^X*Y7&f30< zX`T%y=e1B8kLKy;!vzA-=05rRhJplX!bB_lhzlA#gn5sPL(D-fxOnZm;y^{@h~@iD$h`Ct>k7zD>vTpkzQjA{9m9$Q%R;NvnXv^Kej% zILuW0_fJda>lDv>byOYZ9 zZXY*OqHL?}ABRb<-(NR(p1pkJclN5;#OqBFK`>Xh6GOXj;lwFa!*IV5k&pmlSq6H{ zR!qp|bj^hiaZoGRAoge(Zcdjj~N1fW#v({g1BN?F({Fhg`|-DU~} zLBIn{uHme`MbG!pYU`^n)VoDPi2@e#u(0I8CHSbRY|OQ zFIWT1k9;@eka&(vP>~4ii@%G8K7|E^K0G<0!1Fuq$~Ap&3Zyb{vGqi!i6d|;?ME4; zo&5B?mW;LTsw{e5SN=%M`Q7C>JntGzb zlHwLC!10!5-`6Ggc9qzNq;%OpZqw45SUI%oVnFGVxsZvdU2Zw*j=CoDEuvqmBsW~K zk@tAltFZ$HzE^oNG6Ze6-+8-zOX8g=_w-EqbKN}Ba?QzIgoY%ZMI}u_NPNHh~4|r}P5VMP`1cYC%2VT`=)l^Mku zd3jJz;glfRwRVm)VB4x48O+s%kh=WCU%!1M_XW1m+75`&(myQb&YqndXlw6nZ@c=B z3lasubvEJYusM}~P-1#d1;2b`qRwV|sNw9mfohtFOOz?RwC=^{t8yQyY)&EYS+^>ueoknjXVZ55;J{d5zpmhdcjDxma94!8Y3HTW zPX{-pd5_En;G9c0CN(Ttd}5gQev3AOo2$sK#)R zF-Sap@)Bw0QqFV@_;2uk2*Yy`Cg?%fb-|`8-}mWjuZh^#m^5P}5Y4|)#%b6@=SB%# z6(iU|vYBNJouWQ<}C9-zf|qA>h$`xsxQ{iV8_Qc?u$}AKZEht17R9B9ClUErlo$k-rE(HT+{C?>W}~{R#6(gA;Cdrtf7QtugE>LGDDq6@_6yO_M$Y%Ryvmn1%9&&>Q(q`DNec8{ugKV?b<$7ir1c_~PqAAEsw*HY9 zruvDP^c~{Fn*|&z8NY7-6GYq~%rJ_^mcdfZ-Sw<@s;IsLDGI@u_{KJCRao=$-qWDe zeTzdHGtKuomr)w5N`M<8(Mxoi$XSQ0!*#v7(D3tT zueTOhIM}R8L!_68iC&^3$IG4jmz)(FFN{104kS&b(?gpS_%ixgtkHU2Ql8PnQP&+Q1-Zi#Qtr!A>;SH&P8s zEbOxEhKutPs4p_aJ&UHSiXmqLp4~4ZsPB;MD9xyO3FJ#?5Oa5(n)$iC?+9v<%eT}X zYA7k=5^sB~LHUG#k~#u~e6hf7_&r~jSB%{orsAQoWYVVt7DAmdzfa$U-BgNV(`3cm z{|bCWg3GjzAXWm#&zlpCYMeiFxb^FP5TG>HR8v`%^{`8Cn%B6m@54m+;P|616uFn{ zOgE?NVGZg$hk~|kAX3fHtvqZDWTB1XuK45DlmriPPNoK z0=iI@6hX_Cie*Rwa+wbku6@l%4LFNc9qyStLqk6U1~m#zlt#cc z&lkA4eD?eCS&GqlG`dBPN)=-crltzZ3T*HhqRl-wqYWYNMM1#|B4kfC-8qdL3TFL! z`L2OQ;?5`bPi~9cU!ECC7fu^WMGZ zl|u|E+_;EX>CgzIoW^}*cGv6NTeokI6`tt4&WXa!C1Ez9u^OC%qTJ{n((c6oo~&P$ za_%>6PGZvK%N%hu=F^={GoMekezk@oA^~_&m89DmcKO>I&Q%VpWNAma_dMC&coW7y zKD-g-E;+CySd2-r_Nwqzoa}p23)cD3oG0BP;&vd6*-}FX!U$aUycz#F7J^Ci1H?Tw zOf9X$R^yq&Oq>ZeGZQXsW#tekSuW%EJ!;aYyHjK{XA&FMXbgvL(dph+G2kvb2O^mW z`9Gj}jlLXCl+K*P1Q>)UXgWF?;71(#xUb%IG&50`$@RRe@=mZ+tuZD=#If3^ZG}Ud zqxY@we6DdoijZ~AXfk~B%h&G|I89X{5Q6t=H5C@bg83Xipx;zHiVp$>nSNsLg?=y+ z0>TF(_rorhJ2L$I{9zSTZ}JN}qHX9Rg16^Zl@a3}2baF+*6tQ~y}v)`A`W>BO-aGJ*2j4^x=B z%bHDWrZ9UV<_#_asjrBks|K+k=r}cGl2o6hvq~g4K0Sv)Hhap-u4*ob14*y*M_Sn+ zg7V@$94FSt%k2l_QTD*VD07MMl+jir>$lUkQ&3c-kYiPfjz+(ebld;2*#WaW)7}~( zWD6j-`x{T{z!isn4N*2p8I)32ZJNs*0*FMNw|D#%X;RorP_?;MK%(Mazsh}dniU1n7d1c2>PL9bVH2ZOm=~S zulmAGOokk9?PunElHH52A8|Oyy;@wGXzPG8_aS(c!Bm(Et9-Dg)YsSFR;9BRq@hCn z5w)5urWh+UoXNB7zEavo$|&J8_xn?vF`4sF#rsyC0YS(F1czEniyZ$gBmymK-tOlK zIqGndY|)83pSN6Ti?}!(_N2s6q4<-Jp49F~T{}~Ge!Q2jlV9Q99Su(`l@w8_&yQY> zA#Y|pSZTR>*7!hGbz^Y8K61AJ6>+u7X3%e|i8oF4)vu+5-`{zU$vS&08+TNFQ!qNk zPx|ll{O+QFcr;5TQ`)=y?6eeDXlOD!y}K#%@=^*;*Jv+6`NiGsnkrm)LbhbxPcC>H z6LYd#`O{T75gjL=jA7GAfCMwc1~dIZ_D0#(y3uf@`CRZrVKlPBxLP^Tu4nC05CCa< zFI>=Re8BdqP|U3`QyP3A7b%UjENmODibbO+n>diDuF3%{Fu@wcQ-(6NYUW|fYlI?Q z=^~J^e-?Edw$&=9oM2(GaE?RZ=I}X@J0*rj=tta*l})L%3qCHD#{JD}+s^k%uNt+w z6MQ6odnnfV08emdO+c3L@X)&Lc4+6EvvurqGLc0rH`&&%x)CN+%xpk`5 zUC+Zq_|^}*QWjztF1JM|sPF1V%(xA6xPoR!*9|55!*RnA2oeQhNy$|x@*xt7I^u)g zpcz(hLx|SsXcQ4MNRt{ME?Y)M22?_P?f5rRljGdxN27WoA413jXs$fTM8cnB+j4Mn zo+!;x@bdCX8yj=8Qp$Dmft@GbBGkl8Q&Y!E2RIdNb2)k5l7YKRaa|4oj8#!qZuHzt zhfEe2b8wGYRq9RoPqzC>?n7YLbxFJ3l3&m1h4}*~OM0zdvv!%4@&TFtiyiwbkSgux zw9!~wTWe8Tmg*5=N%T)vUk_Y6FKX(erSY z@U_$R-;z?g8?X&iPM_!am*KeyJvMeO)3w6M!;IUPX{9VyvOm?I1U%iFi-P53b!}}D zR9z;sc(Ry((%MZE;4@Ff;Q@3lvnq>-;hxCT6-!gCnxt%A&WCqSDEb3 zQ_+XFUN@Tr0dx1yiW0c`FlEfu*|2!N8h7 zI3+!%l^!lPIh*<9@k6rO)uMqK7QW*Xdc zWzT%hP6Gc8R-QG`E;Gt?4(&SQCi5YNF8E+Pyn{%;y?7kUQzp&h|}cp(!A7EO~Z*sBN+leCj`nV|mc?DUojEdg6> z>at&B2f;5>Q|Ytqde+l^?221Ai;Wr}%}+@nM}=Frni64hgc*S@GegYWz`=o}t!a69 z0f=m7qdl~xmVn^jm;$8~zTI@>;LR`$w$`Cf!`;wKHmC4<^Cn+(B`ddv-E=Pjz z@uNgT#Chu49g6`N-e|t}J7T`kFa)CJCs>EY90(rkXSz>0n1e7#Ikv_V$lW!dR@QZ- zm&cI_Vq*(Mrdt!@V_5BdX~RtQaFGaHwkvVji)&JKaafcH4hVJX7#8exr3^wKnO>SnxTz%}j0jnJt4ayGh0`0Kz z>h@pRR+26oIf%oc>L@^?XEbK{S1|=d|o480;-C zS?a5FYd+SJh2OI2RI8Vd1cjxf`GiM=tD9+eGVzq`EoU8fpjw4k5)~~_AGzdZGy<}2 z)0ih(+EQc39z3HZp$i4&JGc5_o7C?{%xC=c0|buDBPPOr5^L|_mcbzGAVdK|7HRyZ zRtc?;Nca&l=NZ-4aO8>o^vF-D_DC~OjAp10IJ0_V2Ws&oZbKcUytA{j zzJ?f%KNy91oi|s6@*=8<+rNP2@jWxMVlA;;0HF(bi@dhKFArIo zmtq4kmx5d)+h%ZS^5N}vQ7$e~4vy`$xj z=S7W>p(#T4yBBi;S%aOWyS>*%PW<=V2qb;qi3V(gSU{qm3=Dt#{4QPbmhoKPK0eVj zQU%YFX`0y=tAfSSG(mdZculma*MS7ymozO$fO z8qO9bAk;A6_$c?*>sOXPAv7l_D5!KMve2LsJ`iUt1O&r_|l>Z+g&IhE9+M~o9vk^u4;j}aakD=r=fA~cYAMV^R zzVQ@AAtsqM?sRzLZq9%drB^`35i_=nkls@Ry1DLjBj}~}@xD=k^Du%c<_g}2ps8QZ zq36B=5(p|*32&g-npYa|-9log+=B=U@JUj=!|qNk(22mXkugYdX*YzXrs8SLZcKS8 z#lwBfj2*%xC7mWHr=VRP>4F$a442!*d z>khR8e*SH=y~n8m7D0RRK$i60a4rr({;K% zt(>F*w;Ce80DDcK&@E3pKj;42oTZuJ*Vvz81NtiiE_Tn;eRq97*Rco zBfGQtc{nJ*H`mv161HeDO3A)%V-D5B!?0e9QV5Mj^Fv@AXfarGIndZli_eG!Tes*I z^BB8!B!c9TFR=BWAwxWB&-&6>d%={E2W~GYfXa!P)S_@nb6@|_yKk#`H^&yFxZzW> z2&T7!PN)0PX|;fzt38<64CB|Yb;4=o1N0auZdpTrkH0G#F-$a%&&cjg76;ryk?gPY zWOgwFRia3KrcjJbbZ_FjcYeUegQy2DAD^|!QaFB;Ze^9aPb?JOp?J|(o|jkU++nXn zC&$-X#@yW8xQMS5f4UkR7$s;-UsMIyF=i->-o z-P5h6=I4x*o9Bl{a}8gaXDK6QcR8KCN<3p0rNBBmI-H(JiS6L zM5ab7K9pZx2UDImFs<+pX9DlV(xACUY$a;tG|fd6H_Y@|dH6ru&y!M@A8nbLnVz+t z*tiI61hyV-pt)RA%ghDF;Pb;|kYSpTzr++MXMj|Q@6%uDJmtXhOUR(0MQ?qbN7|M& z3gzwq$mXMglE`wz;trgk7IWF`uxZS-;mDDHxM{=~d1S@azhkgRkrfyIT}wokxP&Vy zIr$ZT7y?PihabXJMw0cxe%9Ma!vZ{X#{{@Y4J1}DUH2be?hyWk%ah9jW8oz9eRsGp z>K;0ywVNJCr`-*B@A_=f!ZdJz1m)3^WMVqe7@A0Yc`VV|$kJb9+w|Gw+Y{oS}s<}meBmT9PUNpr!IWN5*@5BTG z=k@(+_G430zPU_J)&2!!-b~clXVs}|9;qgGc3@cxhoO@bwaUR31a59``<#kmAs`@g z4>AAA>XXU`~@fcft$8hJgi~Baj3M50czVmiVtGD~?^OqhrSVB~dg7L%?p65-6OhsUJ`_{as6!bb;0A+1B%PoqQ@8BsDcP zW8j!od2+8+WGUMNIPuVtk-ww35@pm)m{3};lwMhyM6z(khJZ!JLLbO_irPv*zK#D# z9Tm+Wy^&N$g+T57-HS^*s@Hg*msb}D3k5a5csAv?$Pi>tU!M}tCWwh~DG@;*iJhj8 z*TDhheJa}A+sn<@UeL;zfiBKR? zO|}|%MR6Mj%1J3H4~8k5GatP922CcAUgml#2ET!2{O_;Ua1fB)BSeSL(bH4WAir22 z7zJh47N-g*jt6d;eA2vmcA61^7!9?`A2ImkT8*aa)+tc`{9uIG^!@w1ke%*c8VWQ@ zn?tt&eCB)CMN~E<%(H=Il9HNg_{q$Yi{g14c%?dX{qZfVt*>&@($XxP%=XR0<2mwC z+ix&JxtGWtx9nsW=k~A6NT*(&TU4!MBxlH!IT2 zd-UpfZ-v|N^Xaep4vBs&1hnr`KD$fs`c>fUsO1O91VFyf>>AieByx6hanlp3-=N^| zV!V&DKmN|c35$hwE`vuXsr$4vXlnJ~ZSym?gnzaxcq2U&`6tN7&80IeliykB1ppMY zQhZX?Se^GJG$oPqXx*pZk6asLH=b_WtE!TAxByunetw7g8h6&*kkS<^A&8r*d?&9-A@ahS33Sc0$+M?2DWte21pbaulz&X7Up_IvDx^kIp$mBPZi z>K*7$B4Y?kqI6U(;!N!YBJa*i6;$c-{SUtlg{mt3AhlRUFEqkF6nSM z20cQer6oikeNavc$akV=*4^K&@bEpD$_QW(@tS=JHn}u0w~ZfvDee)FmDSXeQ<*$; zbz3iz4**%hx`!?A)u%F=l51s;Ct26HvqG-;p<;oHh3O{un+qq62 zi$WSk;VN9w$8GFSeU3nI=oDnKt)$oc_GgG27kHI2KPNak^s*<bF zS@R-;P6Ge789I*)UHL#x5~+}tkx_;DSmde!QGuWT{MZMX4d6TA2PHdQQ5K0v#n?o% z*#V{z@XVz2cb}d9P)f1F4-m@K{IDHzFF@ZZ_>^+xiMimlyhnM@E`GH_kv7%fR;x6B z3&y|-NOEkx_H-i#ce}9$|C4>lE(JY2_4Vrl7F#C9(v13=75_L-l9Pqw$2iRLSE%$K z-IUe13G*VS=^83}dhI$re(h#lMD)DfSkasGbn>fJ7pE^G6te#2V;Mv4-2L${NqCuc z4qPuJXa)BU2Pu@|LBa*XJl3v&fN4UExaVdfhxWLcy8zAQkvyH=Z#{Qi=6=Jr(tf1i zXX6D(4N%&pd2^t9-`XmhzHNI=^=CehslP07M>4=Rh;uLucw{I-9EM8Z8uh5DN(kw_8w1MSEp=iZN=ot zT)A2J@-8!ixs%qWRt1UMY*#r%^6YN9Wy6@A_3wjQxwa{$8mgb^&DS=5<#fs?{yFWq z{;*&v!Od-5C;v^(d!5>qO6v@h7FRA*a8D0iO3qY)f|6I_rxavMnYS;woQ`LCUeIas z$))cy?1&EjqLxrKMMZwDH?JL&sM@CNKCo>OoLmr(z|tZm%z&Ii_rpEJw&qJaa%@3@ zo4MEsi^~aI5}b3&g#0x6A;C-!Bs%19AA72BzP!=x?QX~hR<_?Y0DmVuQ%KZ|Hs`}H zV_l6v+?Cim$vJ_=yHyp`Lf9GROu`=k1O=*oGD~m zCo!Fr-MLx(vugWCV} z#+?~whKu$Yf9KB^HUC6sI~RJ#1cE4j{0HQ(xH$Wbyl{bdFszRJE#ixM9{gO;YeD-X zLN-=tFnLj5E+{djT0KJH+^L*Pxm4yE+tDfwHnv{knaX1OVwjp1N%3XlfE8UL5`0ok znWvqNij)02vX(UH=Xtn*!c7q?Kxia*r?=5`mK8|b{y!v_n36LAlC*d4$~=u9N3D#s zM1oEg{F2SjKC0ls+Zr zo}3(b;v!fzxGR>3+uzP7cPD|#$fwY|JV&D6p4zi;0;)(cVZ;B!*jvU${e54&Lpmbr z&?$n1fHX+Agp@Q0(hVv|NO!472}p;6bcb|`lz@~-Hxkm_aJS#z|K7(JpYTGM{LDG~ z?7h}|0dPyQ&&&Q;PLAp9uuU^pFW0KnN+OaP6IbD`pc9=4ZTZeb7!IMl3rNtq)yv*$ zYSP1N5*;7obny2p^d25PdZb;bX9%F56C889Nnnx(g92H*IuZ86{xt+x9^vae3>Dkd2&)x{`D5N5%P@);pGHR$;#(-&q>;=efJfI zeocGX0|)V69NemUw#(KSWOT=a6YO2O)X++HT8B~!Z^~Tm;^Jb4tTMFW|Mbnz+C-ux z?N`ogZb0wVk)IM|jSYjf4W4a3ikqmIy2~o@7QT5z@p)p?GtmD8Lf7suYhVEKC>e)Z z-`bcWsd_PnqXcVXd=p-vk(So)xknvCLqli?+(+O8c;2iX!0;rI(r1gR-E8igG(_p9 zlkK`RIe6c>Exo-;s)S>oonh9VAInM>JaDfX7n^txdb02PqO$+EkV&FIq75)G>(w$M zfREA1;ZyrA@JDy8M2+agX6ir()qK`Tuy-?fF{$fOFm4k6oc0y!E}|Iz9o3 zXFR6k-1)g71ceV+Frd3Gy-+nH3W+kF=MBItfn*>7>i(m}cid2=ZT6Q23>b+cUyfGm zXo8hVSB>yvRFuQvy1<(uRjx`iFdXj*wLB+w+hDdRFNFcb+V4W2<82z>*~5(qi}JCd zp#`Ytg!{I}>a;auU>2#)18gm)!%4R{Q&bcb&(fuK>dkRtMSSMIL2WoVIGCrM4=Pyg z=}stO8UI}|JW|ids$F+>AvFhPOEA`7gn&gc?XM49($X0p=4nsVIBMsrWr8B=)^>T@ zO~@lFi=eunEadV;a~N_wh#n9hEE?}NR`ZvSQIfd$8k3@T!mL)j(g-AaVL#ct#w!y*-x!B8Rp9&ke&i1Ns2~&%pYJ!iAH<)-_W_`xP*; zfEN4tcm)iD4&hd>Usnb_pN!ru_UcgWCXA`uOWLT}a{MO+ZPW%^xa6{klxy z;LDqu%h+VJRwYT;)O$!aklHo~EaGE@o`tVA%N4pOX9TO5k zSamD6cXr_SZ3b3yd{KA(uOkqq-yHS_Xzw#9CY$k)Glm${o2~k?pIyT|Hmr7im$eGu zut4x}bWA?75JO?;a5mp}!%@dd-G633?;nOI(U?!>F9g z=Tb6!8PKs}m(_d&8ULIw|96>QJD=igVM}d0mVC?knX&fiKzay)I?MjyVXV21CP!D? zFT)!z=t(gU>Gx=P-_OG_1wMV__0rt=ZGYwOevxYkM9D=V8Nd({5gJ0yky0{tlrgoDH?Xun>kWdQw z`5V5WWRBeS<1<+ zc6P{kofw}>mS_E(s5f7WzOM|-tV=!KtL(0~un$O3BVf{zSAgRW)9eS@?8O_y>-$KF z$YO1_^;_2@S9bqAKuU(bD^9a>8dwqjd>&+$g`~L`J^uAM*s49p_gA&@g}TcEQLMAa zknB;JOsS*AAO0K^YQrY>f++}bB2!Iz!h-@EkI#Mg(Gd7hF#a}9On<%C^#85;4q`0; zs8AZ>p#h)1Hs?rvMFe3ceMV1{!gBB1ON7m(KuFGyvQR=SQ(VMy-mJl=7$MLfArZ3{ zyc7Ro6qYC!KPE&)!zddcAD5=uenG1%s#GcQA$ZafM4n$J*EoW_7RdBp{xnMVX-av-zAYtIjSP-=@Lk6DY%IaJf*E;msUjH2_aV1-@6CJI6?NN0! zZ7xvcOC;uFcO9)!R21d!?RAD7c!G|IqRO-|Gm8&p2>whK97`u~^$tY?#FTE4=`C;Y zmxARG&PaeLR#sL<&P{}o^S&I0N8U*c;UIVL@jVazzD9m-G5X|+T=ZA}5;kyfE!}T6 zGD|T<7pS&!vDwOLoW}FTJW6YL&Hry%h;ca+oD9i9q>;3;(CNxT{n?y*!r5#chlIH% zhwzVHXN!Mwh>ba8X~I{6bovhN>YZgQa)a>6fvqu&GnS}rXvp-uxT!NQ@p<__Ygp;n z3tcTGgdbzbpb+9{A%iT92RP!uNp-ockQU)_o05nBeg}h`nv|3) zh_RR0pNgx-K({{RV?HRUvlUYT<;$UUwDEp#-ytHD?Vf!4l&wq66EMGpq$85{B%& z_qI?5o%S6I`rFH^)4a@~DjUyj^8ZpK-z#kK8LuFTPbQJQa4$cy z$a-%i?o|9ydq&iR;}vXj-EoBmC9med^$$Id*QfhrIGn1gcm*GxD99PsTb+ChdjGp{ zro{5-pWD%qQ5l3=25Ci=!ID-0H=Ksp>irJM(^g^ z0C_`8dyfDVciu7m)p85z(isg6!rd{<1Vm&XQ&Jk9k8A?39VdNsD_V45KvlP$0!h}7jRz*Z)XUkeW?hH7K@b^dW{>b{g^eL8vu)|_bixpH$8t+W#VN3?y z5s&>&du8?rnn&0trzZ&X*49>NAoVkomavdO!=m8nz?bi47=3&^ad%3{(W_E zIK5Ep0iOs^9R$4RzxeTa4jwH)Y-Yy5W2Au7F$ZRja_Qx zW!@`4N|XC1GgwI9r;#U^ikr80>u~SkjFwAuun*(hETzq!^|%^Nk9zL6OXPp>;MOti zxBeu8M`+SCzr{gCmYtV(+~@6cDYu7Bv#1DKs)`3#QLjkiAEa6$ZkAw5sa`e&(E9>o z<(d?FIyJ_ztQ~QCpgGw%;-OsIYUCA78tZf?0;lsm%o#1(dir;L{v-#zlxS#yy z=|Tz+$8mC3zQ$IG0ltWVT-4O^5q%TObn|qBqusiTm|mUplo{9Q>FJNMbCA&`8VkC( zXS*Nd5ils$I&aht+Z-GmydK@?V#=lLc)dEf+IriA5wc)DmGNAh;vlYqQ_1FD6CyOx z2*j@G*q!$;ncb7%cmHzLP4hAel2X`d8zrMxO;B;T=&OlVS zskExRlF}a_@^`<_GMqh$s#2omD zW+NjsbpJ=}(5~6DQ$svW^#0n975{BR;tEMZo$&z!v@)**GrxUXhv1vHSiT53mAw(y ztA*-Gw;3sQ=whjwHoigKx+AP{WB62kt9GCV4fz)8;(j+;I9p+#E2mRQcFsAyn4r2w zQ(#!!SShuML+khu#$UKSv4s;O1^Po*dzYEr5L+?xZv(70Ti0&(=g)&j8SL%^T zUpD;A!V;Xd={GVWz{mgRA&QE^2MM-TN@CgZ(f!h3)aDpbTw<*5QDH5J%#bY}M_U)$ zL$_bs46p9m-q+EvP7HZ3^sl82<-#?rYfOWN1D29VC3V0*m^M*Rkdcj5+1>rreZ`(Y z?@{!x=C+%x*J<)@0L4n+o1pG8&)r{L((hx7^sBrfNE1$4g>XihsC@nOX?$<2iI33`+lPn2 zDCQ7|7YkI0qPr)mFB;UB_>oTTnN|?WCdwTIjj=9B8r}!@g*X@)@7NkSb?QrvNro@1uSX!vA3oLA_^&2J(l*CoNcsoR%X!ld zs`)M2`t+Mw@i>A`^U`gy51GEClmw>Kwhw37i|Rv5y>qWZk5B6sV*r=@*Ttte--5^8 zc3N?AN$DAoXpMN2-r$nsqh_y?_OiZ08)m}C$mj9wJJb8n#Xd|JsF&0?qzy!Abw-F? zxUMf6M|bg<9%`(AZ(`ap3MHeuBW7Q3xwVPWM5&hR?j{%Glbx*b2}AiK>To_&LB##A z-s`CH9uxFU@+WdCkA>WKEdx;GeBP7OULHUnp5I!&Ay8uSC*2PP)Zn#X-^=UlyU!5zQOPr?|&>o+L zQ=4YKuK8l8u?zvbAW_D^X#MNx*-w#ph7pR&%65ZacHZJWLItMh93LN7ZcUC)O`UGI zh(Xvwjlq;4ZtLIt2X@p~%Ens{Mh?-hEnY2`$4@Qa?l&_Z8DJ+5z$JHV zQG3}~pnc*I)g%#GN++kRN&b+Z(dzs5XNG7dp9S_?P2&A~@3luRy>)1dh78Xi{UeGx zVa?hdPXcgcuP1>U&~bLt1ql!XeyP`qyeF+@*^^xQ{D{rv_R(~MJ1wSu5jER&s`^O0 z`I^9vlNX~!1Z2TLI2_3~yz3)&#T1=oRTPcIO)n-=D&Cz)@(>e6v-o3*;kwO>7d@62 zqO^fAZ<*MxGfO@eQgd8!diWM+Z>@KWwn%@I=*K&YOPwlX{cq8M544MU?0)z3Zq&?g zAZb)1v-;w_xBDfc$M)sYlwqgV`)q{Oh*R{WmfVHAP9n}%jf8=2z#7fMk8p$8dOCBa{nQmR{%QupLme9nFW%9(p z`PtrWDf_&K&wqJtO;Jf_yvN2oQ7B7p`l{`;(AIsS-G)`Ao8o~$Lm=d&Bs7$4o19L- zJ+;)ENXA;v+SH6IlNqnXFD5+pZW-U9t(=#5 zKW+&7J4a8@dy;#!_mmh2EYtPbwi07ZIbKC#K~xo@g~?PAd(J7i$?+HBuc%lXY(${a;XU$}MH^>3n3)QdmhVcGp_;dje8raC49u(cu= zN8B@WgVMD3EKMAfPBlD?wb5q70pV*Dt^WfYf{9kPJmgGN2y{XT$Lp?*X$by1_^|)> zt%W)ZN_HBV8G6?1^M#wFQM_eF$i+xX`V+IJ+)jCd|{AdI=48Nmp@WrB9FF1v&M z$m&uKJ$)&B#f)@(-GTvUz*`F?8IlgRX~RaSz7|!8bcUU+gApN;2Ga&V72?*N>)3o( z&x?ag&^4Q(N8;oyTf68}19T9r(hzFhCS!<2PRYpz*AnV+04T04%`E|ivRoC8k_@$A zdnm8~SCL$o9QHJdj7+CigGo~pFtobtDH2HF)9irwDI^^e3+p4h;dJi{Cw@BQ95^u9 z3~Nld5~zLeKS?#x;brEM&u@D?#M7-hQZ!v}zZcBX4O(qr0GaB@gK&vjIs-cFNmaH` z5XQ1-&YzDdfQUUJ>|Gcuk!jT!drEa6HM4##Pj?yW+ZvxO_z7bfsR&TB9#&TaU?K+S z4xBIcc6Q*-l`{Gl5B7f*I9}`fv$z--9H^D2C(2E`eEjY`F`t5>BCe}>5fm`NN)+QR zR&g^iH&CP1ZW*m}9I$sr@zMBZ{mZ-k_ejo8WMyP*_T+|s0Tya&s*VH>`^4nB;%u@! zp2xGQCTi&{-CFqvU}_)?s~2v7aR`EDgni0A>X=TDd0ebH&B&zttG>|d-ewABEta^H z6pxkd)#T*lPtBijh?uKm^KKkf8|j4ZjB%4}w$Er1(l?8JdNBr2OyW9yGaW$z0s0*W zNn6{ZF|!5=pQE1Ka+ztp3M&mTSm?UzT39f&a)45gPzr>o+8?%G>1dIHDB5Y`Z|}1z zJAg$s`!;i#|ETt;wSJ3KS@Ufl9jly;;wCfx=m<}(_qh_4 z%z6Cb9$aV{jQd&VJC_paqzGU1&CShpM*92N;(u#833_i&{OP&5TcZrakf zY+h#Ujj${IH(Kf@MCg0~X%7sbzglc0z9(VREKr+Hcp!b<^q;(_s55{ajXi~T6>r+(wzvpe1PdFfm%DIXFy`89H=b+}8laow3d?_U{30$7S@nKFaELBq8`J^@lVvrp44u4i{ zl@YtzSAY7nx^;7QD)dbR<>q9eX>sb^I5y4Gd;rDB$e>X-Pf@OgMgrK&O4Vr< z@S4Eb4W)SNj_4PnPjjLBU3cySo?xa*d4@@yLC}E+3eIn~;}yAA&3(bQ$pjAHaGGqE zC;L2YBfJJ!Mm}(HgToK1L9@}wii(P@rZcD@iha?S*ptOQiQcukUy)S2H z{lGM1c>Bt|XY@m!(rf;8+G}BufFf;c%O?32@ec)HC*w1eqv|DF)L9bB7;^XUQblvv z(C6Zy8_m$5$&H?l<9sr~aat<^F50W(ZWjXYgyfiHp3M<^mew-LiBhkQ+@bqEXB$UN zPmWk4>#-$xN}Cj$diW&HCH-DTeb{w)PULAizR?dvm0>SIF>?Xp zqRs)@BKwCg$DPir4ZSWrudb_(*iC-Ir=U<6+6ORMBA=bw>}8{C-NQSVJ*zjW>}K8p zROI^gD0peT%0C9l!)U?M|^W}v_^h8O^qa;4-!v%J+)8k`YGM>P!)y~ex zaw9C**aNFJ=-$lA$FvIdx=QOUI3H3JWW(vxephPCpdo~a z)qty#P8Fb_Jklg@mz=6xA(2~vi_2ys@nIArt6sfTU#f^<1l3pP0~_di4S+aybEv8h zwjrU4wf;sgA-UM4o)kW_6;%Fk-t}VkB>rCS$%}?fpwEn#*qbJVWbWog?^-QyroaS# zC`%FSv0wA@iZt&bVQ9;a!-GmE`9mZ{!x9Z}r#r;Nyf05Hy0zJF-{Ilmf;q6Tngb(? zGHoE~-P>3eRUYV3mhJM$#4ExHz1SnXW4pz4!5{*3m~q%`qx_ZJt}?n zjc1_DP3T<$oNr(JL(}F@#k_WtFQj8(zbnxF3PJ1AXSdg{fQ|P+gJo-Zp;-PnC45UG zSI_+Sy3X>?Gzwm)r_fuqdR#sKo#)ZPmBUI;4L8NG-M(ru?C{gTk&5Rv)s49A{z9*+ zR5H_{EC(I&G`#ocPbywdnBcvI0}dGd>ab0?p2o2nf*7>Xq;0n%W*^?|7o!z4Odzor zSk1ocy?bz=)9Bd-1cAnj%?ZfaCUb4w>QcqMok zVT1EQcA5`4IXO_Cq#He*TM;6ra++)W(G$D71fmjV)Y=o#h!{ zU?=-MgC^Ico8y~XcPnjsbw|WmEF%6qh`o`{0T@C+Eq#2Tg-uG0!^smp^X$dM@013y zzZ=V2d@uk0nwM74Yji(ZHuSzMIeL?ty7v5c-cp4^6r;~WE2GKF>SAoq;UcAHh~^N@ z2R>w187y57v_V@-W@0V@VokMDlYtKCtGLd-YLNNrSdN*PWl$Lygo=_j|MNo=s9N3^ z?iTq2#zPqYq2in_^&G(=e4ek3KuO+uL(9us={uTu8qP#|b!npzshc{uVe^q^%m{sT3 zCxd`Yg;;VpNk|mBKh!8tOcu6Z`~6bC?lm?h0!i(EE8FyU=-6wD-{=U`byQ8{L%rT7 zcL@nA%yuMycY?4krg*f#yc~}0lTAm|f`W-AbMy4?x7uem`Wsu5Ysz&nZX&D(tL2iM zU%up>$medQ4g^5~Oxo15ol{^%@i|Ns}oB9mcT@#YqleyNT0 z@DEKAcHMQ7#hYf^S^>IH?*s(nCi0q&pNBZ6$@p=bzNC6GOtX%+h(fz%X|M><21k%`tlWU8KtC3fH~Ovs z^*Z$$ELK=%BfN&d9L^v92SokT9k(^(L3-y-W^Pzso?vqL$8N0{MtxkwC;scr&6%1u zIA4a?p2YK@-Q#yToP^5w;d<6PLQdy9*=aC~zGFaF|M-K-n}+S4)Id}~ zLW+oK3+zY=ADM>>bO^d~C36(6{^3UjeTvYxWm%L~($wAFn2-HTsfF|pyOB+Oo(TTb z2G_YkunwKC!hP@4b2@Qoz8ft< zF%Qtw>?cX?XQy|v)zJx8CJPs`FV5WE+?Yc0a7Hc4t>b?F{OP>(xQvAsn@YfepIfLL zz@akSaWI?wXsd4i73fhG(I>N?12C8WFqBPf5HV}yOq9g34kaY5I`IcensTBf{TE*h zR$qT^RyO3Yp(FYD)#K9iHZTusITQCv%7(kH?$XDa*S+3oCtpc+@_14^Wi{9I_e#_6 z@t7ouGDkbuLxzM>AmZ}tRkL?+Hh-$9%i;OE==*Xt(;0DibsRI` z5#)UVDtk`H8-@YwYuAoerXM-y)bLt9scmr0U4b1ZCH8~!`FHz5Ml@H^&=xbUgR}a+ zH+PR_Lo4R#B#T&ZqK2pPazchyF_DkUzR2e--C2Cauc7&xpAvAxx_a1lxa_6E!!p&|+;me--*47w11>c?gK<4rV$i;_pSQw`^n zci7Vl)mzZ0mQ% zjW&cdwZD=U(eMl2_vtH6f)%6qfss?zc0;vF?*`i(1~(2_BnaF5Ki+daZSPGYVbjY| zZIJo+L-Pe$crs<3h(ZoXuudHQz$%&)A0JA+zk7(YecN^ac;I_iG}V*`iy&dN&`iE^ z-)&L%v_J7zQTto94}a?A+D8XNZ3g9CON7{YOl+(%2VwfM5fKIJWbMNw?E*b(z})Ykbcq1IH&v;RareVU*W9rE?r!SPifg%Azfft7S<<9TMAUipjhep>ajNhp%QQfAhxl4lM--+?hfRL(2hn+R%O4xUE2$}kl7{QnxCgaZSq;_RR7FVHQ7@+u=j2Vn*oIoeV3$QwE($CRQ&Ur# zM9(epB;^U&(a<0}8_T98^0g#VT0J3GVC{FYki+6*AZN)~$hf=L(t%@2?uNH(DaLr1 z$9RJ01PwaoWLVP;1QHkN)GMInr&W3Y_)@Y2GQZE>iSw4cNo2t-%*IyOk~ul@mwK7w ztAy#hmZQHU#rE=|&~~rwd2{d(5EILA4A{iv;cp)u9UUC>f+MH*YmG-D8d|(jM$2O) z4QW45p%PPXf37mu?*e_t`Nek-@fP~;3s)}6HMIkCSsSlV>jA^@7=eM2v_Cpn`XP#l zuPBa1o$E@eud}n6G&?gJ3mexj4Sj-NVd7* zk7E~Mm|v{u1$^wjF+=yGY|KG)efW9D&pVm&vEW3}10_HqaX0*B@l18NL3hM_+g$Ux z++#GRSBu@Y^M(YYx*Xa3u7UyEW1)Y{Cj$Ha{>sYA++P{kYyONu^h1+TJxdWj@+`Ko zz*!ap5o#M3dU2vmls0cC09#(?|L)U@MFyh=c{b)Y{WxRkY8;`?P>7pu^0^u?l?B0V znaj*Hz}#q5>ie4EYI=Hj zX9AjV(4`Y}`HO4qi*-IvNBh=HCm54CBwa4HQjg-eFcUkDBac!3GS3)GX&l$_>hc0| z_rTO8f% z9&4h7xRKr>l5D5suBVfSK7-mA& zHxed=s+tdZ=$uAJ0?@E^?;u7n*o8`19#D4B+=^i)QO|qPlYJrD)zdRxX?iw7+m!my zQtKZnv{e$oty}b*Qb=$ZH_!O2S7peCoi6>#Ri~8+rFv*1<=a#|#g@o2`Im^o;%9OY zCvbd5>ZSw*H}D7beg1Wdoi063{JhOWSDKix#m^?0?}f7x4SkXbpzn_Lgwh=n7JuhI zQ+Cel`yzslLibny@$)Bet6=R%Ae3SZYdv#WM`YA_7~+j{06b1MWKsOIOt<^FwLLeV zjcyZk2A^be3kY0S@kOCie_|38dXR)Nsc^t;MPX^=>-L&dvKW-{-=`(%7bij!pY3_K z2YF0Wjcz0zrFOf-_~NQ1FOw=QqTQg0)lFRJgmWu7$qg|QOu+nL)_VsiWWnO-||MNpd8G`O|=%`fw;b$>m$gdhp-I#=opl(te zCEn9@RZ}al5x(V--J>*IN%V^yxY}9hfEzaojY+B-m^ZJr|8rO*Qdi&LnWt40@`r>N zjry^ik_1XxM#=$O#v#WSJFd@A2ogWKtbRD^(7+%?Ko$~0L(&x)78FQWH9jS@^4X0g zM@8E1@Fq;*R-hHmRA2t!7l@%Lq=YXUVEB*iCwNKkF` zbaPSWlWt*G{{g~b#>X8oZ$FdL326E@zoX5DX}2+mu^Fyf%aT4p{#*q>bK%@AR zE{=y3r)K4JoG(_-Qc7KY#m}D#S&paB<*Brr#^Puubv>hT>!uO9`No8uQ&pWG4h+FiX5r;oIQv!46?+ZlzOyWoH94*lRE*e9YI- z!VKlyIXtYa)}a!0=Z7xXFu1;T8Z3O9eBirUr%f)$pFLC}Fmsyfp0^P2xja992l4~_ zO516F#vC^#LPiDL-OR6DcF8}tvJE5XYL5H zhZfKrE#k=yn%`B?)@U;_o`H}QUq1{D%{#}#;`vocA__)2zaLhi(B)K2721P?g27|7 zx_6mYWj!@+Q4U~9Hi*k=rr0%no1RlI-;6K{wI&8h5%leuhO&j_T{Vhc)QpajisOVo zxq%)Y5~2%$Gw6#`G+j8$>;3?}7Mv{y4yKAgvHfZsD_RKRqTb?|n3{yi{2SE(^4HW< zAz9GTeQfX#&jn8sGbScV+|Sn+0g>=5y3}ufs+DFw&|ofpTx~>$2ugOYvFQD~<>YC3 zSU@PV&B;pr zv!fZGK9kDLu!%a)Kf8cBhx~4B&4QHtsXyo;prJCiicJ-D1W^x@8r^y4Kn66bEJ(3U zlg0i#|19EkfGL^7m^Ws!ek9j)G`%%_G}MUENQt5#hz7_Gbek$)%ZS~`6cg^YdmGXT_pGAO5YwWm@GQ4mmBo4t`F(!uzq@uw zdF%QOLdsouF+2Ku%80rvU(U3b0VSXDS><|+ocdqBf~4qGkdJ0wnQqlDWH2@&E-fmj zr}f?6h?6(O^-HTTrN8wjx!N(B!OPwGp~{`8lCeFJ>?mB4)m^R=F> zh?}xVX{myy6Yt%QKh7@B7S47@4SdBbwbs@K#Yel5zhOwPim!m1j;uE*HYcksikt3T zI9McEOv-X=$qjB4u@cYtB?B5tMn=ZBVPRgIHB><-(%wy1dzZ5`2Wrm)&_vUexji%Sd1#b|qVS_n z54_giW!HvLh=r^Db*{PVIotC2k*Fjh5MTV$TCMHrg}2!D>;si&Uv0*=3cWLABHXh& zxI-S{N$ zcwQ%VRv^uix}2|-d)nf$HDagIq5g$AM3Iq+kuj3KrouMMJ+y9fb6a<7q&=MCY?L5E zAqp5`K$8)&=!w_N*TUKqQ5sTIco2PZ4HX}okY>n58CqKWbZzitdVZcg828TZx}PZ@sXFvN&xi`&&}5Ajmn8&A1*z6=EN(p z&Euc;Pcd;OiP9>#LtKYv>0(*IyLz~J1jQ$ zI-c0Yg+#2cbf?&jv<95aw);JPRVb=;(}z}%Jf(eW*3`_AG!vC}duJC)NjyZ-AAmA` z4&?Xu@82i!THDmo(FTIT;bUCf2n0W6t8*&d>rU_6ngMY(c)DTv1EOEy^HWG70kYWI zNPYxI8B022dSCX-soI05?VR$~P^_y{WMN|oc^QbH${D-Ab*Wp1QwjV_I3$`@1U zh%kzWmCxiGuT$Z2k(oSdZ&ALX-*jYHu>mXZ=Z z1?k-V{irit6?wN*5=>Bh0juzqRU(*^JF~2A`5rx8lqAzppwgKP0LB zxXv%Ku?>fyJgwZ=_!y(V#O$x6gW8@|y}WtrmaR&Ygt+l&siUm1@n^3-%uT;R5-Sin z)VS4zsZn5EyN2dJnOqM5-;VZn$Q2kWLw3yR*Ew6J_Q4&HKclgkC>8=p1h|^XGkHTE z+}$`@L1MIzZfQgR7NQWnK5mq&%X7Yw_M55nlGW8sq?7ASdKi(9YVhvO0=V$6;9Lm_ zTk%)I3*SFiJkre9Pq#J$i|~#6XomU;MwXLfmSCo>jgSa9qOeu>bf#6P}qL<8Gdz?D5sNT_q%*bW26( zhcxd-x9%mr8P|+4)-X?L@=NYapAXA+uEuCxeA%}(8qg?+o-%~Gli`&4k+3x~Fb1Wj zMQuAsX)`@zhf*lwU{2k#YAtGRj6Js~*5uP8$u&LgpES@h0 z%>BIQoJn&Q7dPUihfNXaWAwgQd40(G5`pk&!bZA5KmW~g#l5IRrGNIBbv60mh^JZR z+M}5<%psWH+AwamvXt(rqNMzy(G*p@WUMhDM}o~f(@ja9NhOj9&9y{>WBb;En`zbg0jwXwMo9~SbBtQC2J)>hY+JN{7t~hl! zI?^cjQ(oc_6KKAw^xrtc#R25ofMqw;xphU^yb&Y|1EvN0t6KcH!W-pEJSrE7^HIJ{ zuF~iSTJ+a$Pttc_GjNw!Qh!xVc<}Ue(9k~gFg1#WdS{;0552?D-(O)yH%_tOwYl=e z`#a?0p+cY z6%D&pfyD$3OBKIF!-kp!yVE{-mPLrN`(6~?ZukvvL-J$In{f`$X#$b{)cmjH)QNLM z15mp@ANSj}%5S#zKsZq`YI*N+l}zj^6USFcf>+Pr-aR^Wj@#eU1Mwx9RB&*y16ry> zK2p-`zrnxM0|=jNHR2@X;l$N_4x_ZAC_6;+a?K(|C@0N#wkeyl)q5fJSj>_+728tC z9pZdHVZmdri|eKZYqcq?*9_xLJlefPS*giQLfMOUX-jT1c@_CiuCl72QO`En-o`X^ z-&rpmP2oJZ=#H%d;j{U9)k4+F8LbTWoARpwZ9Tj@>NAq!B69Fcy>{z|rbTyZcPd|> zUC%Q)TLR6D%P4yE(9*2c?>_eYF=`$r-IV4!v3;hoRN*4hq;ZR)r3!g&4F(>T%6_(e zi}bBcgX_kj80BFc-zXbBr`Ox=dGKdO+*Gt6E57J1Hd?i=pC{eeJh>o0_-KRjvrcrE z80+?G+cR*`j+;jFgNy;0qEW}t(u zIIVO1FOIfNlAFz&2#0tMzP%Tkr6`v6;U8u~^GYs)3p;8jyfKYz8q?F&SvFXy-B zQYKKCgf_kRkwUOuta+>XG*WH4PTGO!fjyx01uRjh` zUL#YGlu~BCpWoJLV1LM_-BXl}G190w65Chgj8(k+Bw#J4=^`mw&;8BwigtaG;gPTR zH*8#1w=OPEU5>>{I}l(PRXn{?dh2bzMGcpU(fLJjxsU&i{wo1`(KVb(jqS4wB+Z*g z?f5GO-E3Ifb~sN+TqUjK79Rw(_+A;3{O@n3?k$N;V7(n9e0iVXmLu>n^xE7OWfcQ_H`U^AU)9`t^%>v)p!o2sQTl&{1N^5tf>hBL43q!;+PS)s{fqy; z`}YI*um2z4&n}t8p1*NgXm@uA%doBPZE_tiK1_hBZx*(*3F}cij$;rmoTOVHqC2bp|0zP#a#26l zBRB_n!cgNSP)2xNCZ60NL?CE|wM>b@s6RSC1p@{rI=sgJK^S*@b#mOTD{=>+LdHGMD~+!docT zT)*_TdA&K^ySoB5zs^r?(A~Gw(K4c?k%~F_kRfxM@|d^7k#EV=^xx(@k)?bwR{8r{ z<0M3k6eR3%TyDsfvvjkGSZp=lm|PvoHuT8YdS2XA1FEoruC1|hm;Q->he`zWqWAAF zzq_BNS7Y<>%eG$zye|tsP-{3o%`dLOQ$xENd4Pyfdm;(0sZ`#V?r(%9Xamv9L?t_U z9s@8K@|ZYe&?p3G0vGs0?LV1pzOLQc6SljtSxH~_g3s0VFu6uLDCpqt$k6Ml<-OkI zhjrG`fH`Rl=j3Hw3m3ADlWO6B5_@lVZ^q}Um8wrvJ>U6elXBIc7m~>K_+_{5vh_ZM zFKTgdsg0OK{S&EaC}cvAng&K%M}gMCtzp_%?+H>G$sb;RMi#kR{Llk^yxeljGA+WM z`Dggi^eXEh3jK*BM07r@H?!^tcfoj?X^B864pmtCqVlT0vIZHlyqF!GV-9^E-O>keg2&8&g+Tmc)xDYI$p;n`ow$EX_t7Z2mD1 zs`=IK_#&`?3pD?U5mAzT4L(qMx`f0ufq0VUxV2aGB-C-;ud%M~_S%f|d}T_@a}V_$ z2}^&RXLEdU8HuX0hB6Ty=aKa)_cnW`1|e3sY!DWe1d6cg%NeT)p1ApwYm^;d?PfN+ zEbn665f8km8*)GN{8yyD+^X+=_GQXTS?JU3-bxhR)7aO$i*lNpc$G7HNM8_KYZt=X z13IoTnq#lpsj_Cre~ELRqG@z;Y;lh?Zw5x@>Qq(L5OL&CwQd%&ypIp?KR1z!f;&D5 zo$;noLrHToGdoO(mugsIt48hpO^YhU}sQMKhc%6O)XF$*)3NE5$_rPt=7*iOg{fY7Y{TFsPqg3k44AKu&@JeI7MbI^4+NxHn6j?X@%^~&yU2Y z&W4vMJJ1^VGh@^wW*zq&K;o6Zzuziu9T0pob!|XD)?9NK`@n;pb)zJ`Q(0tsp^d~K zNB8Rw1(7o_6f+A6S;BNq#9cUPyuzSazQbNmRaJGSam!`n^s&v5`?Vtr+`Bv0X`&4< zl&#`xke7TZ|By8(m z3o}YSrSk7CojeidW;uBwSYPtSXIr-9%Y==LW>Q7c44b^6U(*{yIOjm~QH{B~jC#AJ zh0P^?o>h$-vP&~E{LsHKu6a5e7*Ks<{A|*8zfPpZUN8H}oixu~`|t(2AW$Z>E{4qz!N5&=<4tR3BwkH54%v%Ot^Pe4F34pumN0N1WhRg)tSw8 zzbiW^f<>+RXJINaVz@Q6mPt}&J0|G)ozY+x^=&HEy}+am6m!QTGwb@FC579oE&Gc=gdq*7mZA|%R`%IZIsa4Y?!qX^p{{v`+P*Tn)g6$e^9fHP`(a)_ZY*hhm z*zmeXSypos*6=UlN4~#tjy4}Z+XwFkpRct}dvA2vvCpmw9PgwDDk8 zEn{~yJ`-X}JXTP?GH;{PK`uY0qtO}WgA0M0n-L=lyqgm~mEg|K4>~Z%dy00KYV)GMTTDa%zC0odR1K0IM6)GZHzWB^sw;pZ9$#5r-YYjRO$$|+fY!G+|xl(6g*zq>F%D{E&x zkp6n&nP(8WBO0twujEi1tQ+Xx{e8(`_l#I9;6nr^eYlr)^`_W8CjKwpN-Y%$2?-(P zI;+Z^kQ~Na?@?16FT@79h`r>Q(4}r7Z{MzIQixL)C2MqAErYz9ueD#z+ZJ~(O&SWW zZhuZ^q~d?Y!7i4D@31}l8T(+(OK_KBO^1CjEu-$nIVzLo*{(HWM%IpE2Hd@k&KYNx3W7#Bzt9?%wzBI zy?ejkpa07EW^W`8A2lR1`^g+*Oaf-c=a z<9n-2qY)1YVybFVQ(D?t#OKJlv{T^kT%VRUrF+wtFKoiX`>VCDh6=Rz4*yj)Ho@hp zHTFgClsgr3Z1wLpK+>z2(y$P`w4$C`!xgiw4_E5oI#(WC*-aC1yp^qiE%e2lRNQ8i zxS91bBN#PJ{9b8S3*zKhv#WGwyuRewJa(I%Z!^Z^fp*kuUV$}m&HbmmO%g+7WA~lQ zQ}|1h7QN7~|BzZ%-qz!7qN>O)!!jmgEr-BLWc4^Zv`hwJljB^@?#dt4RI#;%1kcG| zIhD)9kH_=*g1sfD@7=_#pM8%(Z0shG3%?wjcdAnwyhEO+BNj?>M^ya2KZ%U11TVJp z{YO=1v$m_u(g!w4@03~{>L(!-Sx_4<@b4Ul;LAUPw_tkr8bzdL-CmBqZ0%>JK-jN-hG?tx@>+k(@rKm^tz zuUgm>E&7W=95rD_dZ-*bYhY*M**GQQ0^S}Fn8(6^BmJ#-IUz?T040Ye3r!5jWXsRj zxRTk1bv2Egu6Xzz-(|x5{A*NH!^(wv!j-Iu@aw0$f%eUfr&T%$1T{!T@%rXPK6>{I zRKp>Gh0=Mg!G2M@#~FWa4#FP@e9VmHBO&48B(f&cXO~=P{>(e8xDtq0t@1g85V6ix zX+t|&-0(mNi-3gkrs3+Q#7O1iXPPFF@)RtnThwOV-%`Yc2)D!T_!rVVx#rmAbnXk! zmlW@vI~tB+?Ag+vAVRqLtnTnzQR<~*ss&Gvv}#V%+vlZ&`In>J&s>9fYUr>680m=` z0sH_{rN|rNP5U?QHR95A^8L3(fpbfW0Oy(hwWb{FgK1Kn)$PMQxYX_Y0ARn5nJPbT z`6eiynLq*%!?;uPB)`!6b3fXDl$imf0^kBmc#;1Bp(C>?L^j+d8jUTI*MRG92FR56QSq=jFUyk!PuD(Orh1u$8)A|5&BnMDID|`=a~I zm+ICDU7hQ4=h{MJ?tYzG)ofhRJv};Fv9~a{+$58Kd=p7^O$?g}Vz5xyI6)SZKDxmb z6+1i+eO;DEl49gluP*OTWmMxa$?`3p_?|}zSeQSjrE2G39>Et3;oI{94I{_BBdg)w zlsFLtS=vj4vDap2X=Y`MoqR*Ue}agS)nFby>|B76SkKo}G*Sp^vFKTckuN zN~=>0@&*x~w#7FSwT6U*9BjPVfBGqmxf^!BBv~u2^_TeSCHnAiaZSRNV?ALK*N^@49b{*Y$b-`-Ug@ zfL`cGAOV%V;u%HPH#Ib?(k$;LBAZ*DrB^>r9Q*+By!8)V5T%172kNccq9J)9bE-axQ5^=)@t+=x=*4< zqjqte!NYzKlZu|bDtn3PZ*H^HU8)k~q2+A3E>GT6i0H<(?{9p*UVa%;jO2AQL{Z0E zTh~Q$?~0ZV9#GfaM}A=LjuaCjdJwh{gjtmdG;>6+M@fPrx zc3i}ATBi>!7CnL=0C0=VQ1Yu_evTtGK_-HN4L9;Zkjatu(1J5Iw&5U_bgZoSo43sP z?d?X|+VIJ)wwlXzq1ZXuIK#ujoojFAPnR?g3lbSy?U{;aTKVO~DwaLBT!+s>S)k9{ zA}oM}ib@%IaGEdh#Hh)AsA5T=!$+v2%xV;e2Fb1RtNnL+2#JkqBGOHN%@Ju5bTsl;5r%?o&sB=TV z(M4=DlZ4&!Y=ToZt$CKxhAj0#(+ZH9J<2YG?6+$v@cARSxs|-}eaW*szi`-rC zUi%?m&-pT0#b6`-5B#39%)G0Wd~Bi@Z}{sE@oU^G*nRK5w>KQncGf;sLEUoYEtxLv zF8i36_$)boy10JQu0DWLrVBkJ68SEQN0oi|c(>r~>>XlxhL920JhX?T2wT^BC>Ivq zPrGy#^;mze@V?Jb00L8Du)=%l-6-$P?BvF*)W_qp$U|?M4w0sl>{HqIz6TE3itGX3 zm)`1+Vv}U&{tH-_MKT0Z$gx{rXVM{iZ12pr91LTR>K2&p+9P>@KkhRgy-Nlt=O!hl z|C_)Z2t0VbM9*7)##DwlwOH;eFvqIspt5QN6xn->~p;j8cp-L#Q5*VG09W0X*8gp7X-Blq>l)ysj2OeAnd zOg!g}Ime>No!?(-Z*Twh?OPcqj!ojTa27nwwiR8}Z4r?~^oM+!Y?sS{B-y`b-y>h2 zoL~n(aIG`%wI`DyEqh%2C|-`>$`#7?Pa-VJLdC}XIWVs3a(tg|9LPsZ?08+Qw7gFp z)=o0Wt!)C_`wn>QIqDkT9r25omS}C4q~Jecp1uVqIk|^pH%V|_&nb%f-hveX%e)_& zEAZ8;?JMuDY`#?>AR@viBKqYr*B5^#8c8l!^rfp{#nZ|PDf4L1dGY6uXD7D<{MogN z@NQ7?E~Q^A9C2_FbLZO&R|7ZT3hYf5=tS>ASgfD^_rgzwr@VY+sez^5vIxSY)YN8N&?%q)7|0ekD^|XZ%ph~g&Es5f$5LB-ZfC| z?5$PD@b{VjAU<5L$9QhM=0-Bg{IkE37ptL8p^_?j&u(HEkmfTYMUeH_=jwNdSwvQt zP%nO9fY?vm(G<`)osKRmzn7*?ml6}L3VDjEmZrGhn5yA-sR;vt4v5M;~9iK!;oUMNI3?K-DKJq zgX*6=pieO>ukt9jSZC1fD4n~0{Wi?8R%zCV6GR-pyIET=0fvZr)^%;Y-$j$+Eo4My z(MvwfR;0X7LJLo$0zl4z8dt%&RZoFX%wyPu@^rWB1CxEjrarjJk_GKz<5Ewzd-QZU~BW{!1eCL4PL!$xfEss)eZ zzq-9y`3FD|J-V3dIC`zX=7j5M4VzG(_ZIqAmDY%(6O34R>sD)@s$MduY!DoG%*4ciGH6fi`T~8m~~Y$Xw0u%9M3jX3_V!tpX^?Y zlwOHk!yWCu$Xnx`$q_j}ndU$ojFg5$P$isY>M_@-4g&&GA$(P@!b)95rD+;7EGWbx zdL20r5$z8zvoLD`T*(l&0DzN?IC7SSA9o@*2IgcMAtfUGSI{FFa?1m%&i7~u4c^+3 zkwo|tZ0vZrwP8+;+zA6;^73w=2%~^?UUII@5@b@p((r z=~($<7}6CcYF`W&x(W87P;iWbVSkfq3Hb?%{Qmxacq*y$cddry#hk5**{ zqpU$x`|eMK<ZEOfYWDb`@z$NmMhXL&!KKT+!i$| z=DB+}2zx;!l7xcp8h4nft4fu*UUH7SoE)R9Xyhg7*;!JFK1)k5zMU2J?YKQAfnE#!X6i?bGG$DM@L82y=Hlk zoScJIMoxQ(R%J67<168_xYP3bl&du}7Mo#wXMNXQ2{lo(IUI4rkLwO+&(H40-9HAN zyWZ*)?I^QY7KJui1g`Wmn%{!G9G=L3L7OY_Ty_cLyb&y5&kOohp!hUW z>fO63KJ9}!1)08eU6G9qctn*@W!aW8q0IQ51F9zyuPoAajk(gPU*w*Cq9_Rz4bW1i z#E}$a&N8U(1c;vS%OjVzt`86v2*dF;9!ebB;9FVQ^m`OkuV^hv$!?I7Q&O7inX}ju zn>8E5#fc%6866c>$t|~^7GD0H_t~o#dc)kTtcDW}9`)JfKSe3NB0dW|1^SguE~8tN zAXLFjeZKMRORR>+`Pn57ov&~i1{K{Py74|A(78jTL zO{2zrz<$J`Jp%v9KL}2haCAB-iAq(;&9_NZz$fhRGXYMFNr|`GwvKyCz^IwDWoa`M z%bR#veyoh6C=?w3K_(z1!pe-*V0fRB%7g-=Se2la+x~@N?MaQ*hh<%3_fNs*`k>Mz zBp~{H4ORsFtgIai&OgfFx_rBOpry3EwMEp5BqCb4$V!7pFQY2`YB#F4oRdRP5XVZv z^(W9Z?%Q9zs~j^Y(mXxgJZV$2>DDb%Ai>%tJf^@MtPq>>LPH)3n6-D>$fFYCSK(6Yil9I5ty&YG;7%Au z`|KrI()p}1rdQj30WTAG?$%xlmU=1aba;A&^0Y)V%FZ_DyWLu+He#V9MhK7Xkk)VUREwR89zxX#D*YIgt3B&L&Gyt8TQzvw42h9 z4Ednieq3^AjU6Z5I{0!ko@xMMxgAs-%JRmafAHE-2Sm}(F6d84~`6qM}i4G+@8SE z1D8uAz0~Mhcdqf;>5of>n8!+wlpa4GbU`g>yhj3&4e>rY`j#Su`W@`F2D{9C&)IQK zo72?L2r*BeB*e!*{JZ_D>J@P|Gbg9gc8aSBPtSw6J8^DnV{tJl27Gz63?hwaXFDUK zN*nDDNW6VoNsr~{pyR5On7~J<(A}$ta~*HJ+4%h?9QIXq<9m}?_K@^OPEHQF!K&={ zkyMnFSp6<5cPIYTxfmHu0j_kcs+Nhp_SP?aL@=KW^(g6gEFK0BmL&NY`DhPmh4W@J zi-Wk(t55W=owI{FA}Pl1_NT@t@jQB40X- zR@iMC5!$VELqFJNoz%wp7grIhm9p;8T<#Y7@TtrN0iTTjO5Y*VmPE|ir7IxESoqOi z^3c#s2mbygwQm*o-?ksIC1b@$}ja9yUv9tp# zc(3I=JBdE2%>y&YO25f>5<}V=?6W!g1>h@A3qO1|54%cAcmw-0-@cPPb^e_fV=1U? zSmw~)5z&12<(RDNhPlmabwx8Xv*;MIRl3U(89!jE0ee$r&Fv@n_>}9lxpM%UW?nKio^jPH$tPjS*APaSQT$o&G`a}Ltk@gEPO?tVm;>qfKeUg;sTVv81l*w># z>bosNPuSY;a+7=BR$F^bV?p+&r0IMZWwZ0Tclt1TvcdNlG6n?rRZ4Clpu11dK`0hq zs%iZe!OO?{H&iun%i%&_MKaCQ% zYgXP}q3zuD*p1^cw{8phKpV{d3COS`m-gM%PjgTUp7Wc>Ttr&wX~|bDxnv56z-$|=w*_7)F+FCGnhoC; zpU(CDyJE%4X=1AKxu>|V#a)(deSM8=ZEdx*+%v+97?8-%Jw4mCqP1X4DJ}hwY%#`q z8&$^J;o77U{Gh9sOTy=n>2%Doo{X(;-y1EJzk>{z#!N%0UR#dNG|OBWDnM&d7z#5haB^}^ zl)eJgfzjK81CVe#(1LL2Ua9Lj7b=s&y-7Brl=EFZNkb$_VvhFHr%Fho8p4Ztjt&K$ zIX2cCN6q2Ka%s(b+th&^!*3h9ue8|fH$eFXFzHKA&!eeYo1bDKA9ZK4IngGvR=%+- zu38EU-M|NbH4>W@$M&7?v-B2L)E&O;b8eL2g@Q+Y9!q+VP{yjI`7H@By%`)387nzl z!skWtxc5nz36U+>+CoK7aAtxq_p~&6{l~PuSd#E?r0;9?N(&iB&dG*e!ja~7&2Ubg z8A#`emCs1fy1h^ntt(aLazhr$KG)s`v!RE${=w({1W^|w^7CxBl(m1R1R2@w|6bSz zJbs*?lw(c3>1NI#2a2<2$OiM^K9yilN^3Bq0o#?z}#-vy5Gje z4ZFjru6n-`GrtpA-IJys;q3F{-c%R{p4=`+{BI)(oq7)9|Awyr|NbALgABj;Wa12ie*c))_SWm_mx56#sp>7iBd4?NhUGmLwj>vQN*f+IR^K@JQr7kn^ls z2jpCj9ca#W+B5!yC<&N>f6B4@Mu74+OjULqb&ulsfL%@HifteXHlixDXa}8wN*~^m z0#Y;=-6ObER=vH{|4?@9h z_vC53IjQ5b>(BmmR2FJdn~k!ASlI?f;JG-6I*ZY*Bl-_lS8vSx)ua8cY<+ljZ@&1x zI`d46TBx<&>bm90?d)?{t(6)_jMu)XUe+bWPD96#k*9l?8N9pJdt3R)7o=st)(#|5 zWE5dOsQOa~hpm#8Rfx&SkXVbdHiDC&n-=keh9&mBMyAx@g;w!%Pv)1xOAb9WoCd=(r{~Ut+42tk41r}Vv4>Yt-_f=>ZWp&zsV$P{omIVrV2>H6ZQ_$on8p*-Q z`SfJ)Z%Kd2xc>A*9(09dY)E;+gUM`r;QCvZkwaQn?BTH!piD_z8Kxs~w&@YHC8 z?LH=t0Sp*creBR3Eq?#&gJDXQIqFY;e@T%cecqE#GImB&Kono>O~LGX9yjjhFogJ( z)}Ab~Bidvj<8|7cG>m*%ZIbZoC0?EjEojYOg4WF6w{XmQ1cYW3H4f%6z6YkOR7*=s z$vId73~p-W7QL4nguq+s>8pKi_?RtLgEJ$k+(PBpQ4t6NHox=o^8)8;I0~*rzmH4Z z9a|}ex+fPL(r@3sHS36E40K+a$hL^Ek!*H7Y_I!?N=Qj4c;V^bP{M!==*rDar`a84 zt45u6wMH?svVO_m8$Q6+i;qi5AwV!PGWsR$lXkd&uO?sYP2V4kl44m)*jC%RQgQs!S6F0)(ObAuyl)JN}b0WTS^B{-fEG7r9j7|2{dNKPl3AhES59%QK5VdYa@tDl>ky%hgndk%-LvpZ>Q}NGor~^f zMqj07ta0*FjAGkJLLa_D|Gc-?21-cwm|WA*VS69Iei~3UtKC)A)I)plbs7PNQJ(aR2ftGYxipR;tC=)|OaF>e!kx2Zk zS&?AtU9M7U0H3el#j&&&>Kak{pM;js#VbZS^L!$rCJtNkM_Vy22k`m6Dn&=$p9%x0_*uG?B_&;H0 zV|(P_Wbwh}kcEUdhR3n~R%}Q>*SmjV2GC%#IboBK0~QYpc`)?OfamD4 zroQJH-(~5Hqw}gPHn>grt_mv;O3aPp!n<(E;0)D-D;(vY+;zvRP>14KlF~0JY!>^K5iem61p(u=hl_U@-ATF{k9G#w*bxk?2~;J@GkP zK(i~b!^=8Pv2PwNHIsL6a6lR8OCGemp*E9Icy`mD?9L+@b(3FTF_wh}%|#g{0Hmtx zU3&@IhWpWPU#7TmKdEUc<5XOtGWJ!^c8c>7n_@z;kRmU3Nlve}0ZrElUQWnL=5xgT%-HYo6+tSqp%FEAtajibV>VprTVE??MdE7g5$UQZW`(hgkJ z%xxSx=hD`MF(rtM`jYRx7%emZEFZ~DMGwc-_F9|$+~2>~xb-huvW^HEgB%EmNQEro zO?Skl#f=gWfO(63|0MF)bPke=Eq*4~0`i+LJt~xo-q4dhg~SO0R;8cmU1K1*cu9Kk z%7YXvn_wD-BZm^_HCi-qHfFXJ098dIaW#oG4JQr27Ul+|=~fm7T)%kD;!h~#a{qpm zG!s*^8Y%-yJhwsYRy;M{ai&yDdnZyp%x zjq1%c(diag6arolI!+-8iIa)iS5jv?b9sZgmkIx&_ic~~Pew)Qs3-wHn1=@XiJ947 zm_I`j4nO2Lr}6C2imkRr+z=+d!N++^0-Ge9S~L?3r*1Aa92u}W(p6LIpLj=ULmhF^m)xS~j|16DPkAG2h=6t<$BGs&@JV=P|B6Zjn6Q-H8 zu@+b_?&`f1H#HreQsaU8^ZolRo7a80Kgw#{rqA4_{f5_E{iHUJjdf1SoDR1x-u|=i zhi07-xWptf(22pADB@j&!6|CFxamYuhdFYNLhe2i3$Na7jqddDA5689q<%iFG#&bW zNB~p&NT8H(KB+-CN^|>s9^FJFw#HvB-t%=C{cA3za_;!;#KYKo@9kf;zjJfP`!^VB z1Hn5Ob!O|VsQBY+x<#RWtkcB!3fKr0S!2KL-HN3_yB9v=0kzx1CuJ6UJ$fMjr9JOGfbR)59)g7YW(?%k z(NZ*c!Z262h(_%B7t}&BP<}(hHKVnnNMy^X=qt z&^IxM?Z@p_scsZUMj-L&^MBxqPIq4orF@)FtXF91q`|#_))Y%PJmN+ zGfmfUl%V~>SMPMH9z#h*6?I)ar=|v6^V0KLOEQC&oHoKMK1U zftNhp=WKMq9K|em@Gd5kpL!#hDdD-E)%YTD`n1%GJ=<~8O`A|3WOg)jT3=6+uwT>o zn%$p@CF68n`g6wZ=0NYqol8r2AHHK97hZ@oCbCwg&!<(j!b_)%r5w&r=bCB!yjI4` zUzIf{Z7BC8cutKUP6g9>{OhFmTlAoNXLT610G#T7o3>Hn80!^c^&c#4AoH4B)-K>N zX!^=f4<~@~Anf0!Jhuyf=j=kc9s+)g*hldc-@kwF@a1#;G0(1SZ#_?}4WTaH#<|CA z*`2Dic>eq*mD#Do_>8W5O3FicgtR3-oqN@I4U;u`;H3--4vxAhuov7dC20L&8UE^@ zeDCSTef5k^{>Ur0bQ>R1CJdS2DJ|BV@M_Tn*EpY&&{CXurVp*pto<^8Pzx43aiTtZBcng8FCovQ8^HAa}_2n$D{`EajL@CJt}pnFo8!v6Z7U9uT2 zn04X+vLlViGlmZLU(s?oDv1OnbH-3d-L)S0qBocWzJc_6UTF_%oh(3{8(eChG_7>7 zH}kCm^2vZi^gw&JSp+++*>StM<%4Ol{V4j0Y|nNu^t^Vrt}&itJ|HMge0Ph8-KKoU z*8rU?>Ao=oi4ns^2G!;(@rLzoheGEueG5$+W4$sZa0sQT^X)7;b=oXcxtN#1g*u?0MH70|_k0lMGV`ApE4~gj{;1=CA;NY$L zz3z+x>P|9{ir(?;c(0ZgotdV>^z8}Qq5pOeHFIAtWxidVr5wCk5`v4P)og-yxKyX2 zkktRr%%Xb)y*ylR!f$(r;XDdHn_#_s}Ta5~zil%=&Gau}3G8$34Ey(*su4 zLnw{zLN0?pg_>?ZezOBd020^aAS5LDuJzb_ycR;lPqG}U7a<`be*OWvvgQzp%>(i% zS0AwCQ0pAmkAD<%-8=5q1k%&&rn{5VD+#)&OtwF752O|IS_p7!<$!_fBti?Pa;n(N zd~-dCPD)BDa+v|*?864%QO5E|h>qr;1YNTZ5~sgoGwNYTpbpRo>NJ_iR&6VdO+g+q?@4Dp|lR!483TG+X99R2-y)ieoFub-<~ZCUl0 zrg5}pq3Jt_+`B%p`=LnFH&H|DNtWmC`wS9oZLg;O?M*vFf^7-V+e+|+1(+mec45Zd zW^6E81_p^clUaJNEbg7o585{!2BDTB!-&t93pR_J_LeKB+XB_(9BGyN+{-a3D!JRO zS0xFFh&DaImdWrc2BO=b`WH}@+P0V$o={YKt~D}jtm=L^lz^(R=5WupF~0@O28R&T z!jGOFPEeJGAR$ZRQ;3I$2jt6vSHrHlzT^vU2G>ar#nxs?)c*ST@mW1aYOAYN;x_OP zB#)m)EeYf#EOpDJf04H2y%qRJxo}r+xvRXhk6y~d8w#$AFXM*S8-wa55Tq!P1On*} zn1S03CN$A(5FiJn_B-r+>^wY}Rz~_eVf=QhHITL=N~?I^+A2lRXg3;BnsJ8R>T?XT9&0pR^y_H;?Nh3pGQLG{rRm6KVuO^4OGdxCsU0)8 z*3%%-2cof9g=rrua`NrPl;UhH#SKrvbqw3bxAtmBowVv)a|gsT?7YgU7lTZY*PZ#T zAZ8jG8ld#5?pyKkJ>Au{456i1Bn)+Ug9{KyXw1O9toYtmysTKiQBgBu#vy^@x4PPs zjpY|Umm3ATFvA;hQ+cm(u@GG%X|wNdx>eN+)H<0u*CI{7!5~_sCvHlV;mw%5x*{K* zQL%kX#%oJ~KHs~`<#7Ls($%+8B#dKGzzUxogllKYmgR1Su@6_YEl?3q7?EK8QyEe8uQ7gbBRh&@*(Rj&g z>vc^0+!K_`BW30&D4J!}E8gU<9SRc-QvUq{02hddUal+Ia_S_qwp-8EA__@+J?OY>= zY6#$Hw)-cU-p5OXxRBdHx|4|O#FIgO-oH=*HdN?Yc1$0OO!np!RW4-vdyvdLi3$y?VImSRW?0}xy zNxMN_pqi>PegZLJC`AiqpCI#Da?UD6n(y+GvGDoPSuiHOPt5qUXT(itA@0qteEIhU zoO$@vMV;y+KR;2>(IrPm^Shl*Tn+N>KWsYY7wGFda1820&x!mrl6I(n^E}v_|9BoE zg~qDwZ_r)S)0lrpq9R+y-1qTxZdAbXxarVBOKS)G3b25tp{Gxe{z!X`^5l8-ZM-Id zjY?r7FIfDWnVRW9%Eesnb!8eM1!ZL}ASZ=}j_$1FL`fcG-9NnylkgM)`$*KnEggjj z=(jir%i><&rt>p}+LWH?^h-@m4WMWs5By-OpcICt@Vm^*{p3}u!frX(&$K>zaMk65 z`5~&**ym{JQE}&B&f1!B)Un0hM~}EW**j~;pyty);~qb!cf`u{Cmm{ROSziJg}9iY zr8AvrUO7+TfB&+BPVm`#W<>$Zz7yy9+^Q}%qzterjl9p=@#-WpJa_(m`Jx)^BX-dE z4Fq;a9*cc}?*aDZcDZRh*NvXxOSm5awk&=`L};O2xrKuouh5c!>b+N6^WE2_evZ`n za9}(8p^wv7_vSO)F5@yUF}Oj9T9t}Mo(79F!v=Qtv*6`BWo5R9Wdt*TkU+mLT?KfxJ`S(Dg_`PA2V@Dz283;i>V1I2%_uNpD~ zOUY+QkTZ@rUQV*C2maFjF^C&3H{TNxW%-5qmA>-p!SF6nz;@QA6Iy9IYI<+JoT5L0 z{>7>G>4RXxHK~&G{;1$9nb6&B!@Mc-x;QHjPhb4vO=5Y-e`5=KCO!8sLOzQ2^j9Z6 z5j|h{oO`Pv89CYQ@bnF7GKEjnM!hQp0 z!(Sic;~LGl7*2y~(L%?<`|#jlUWAjA6MX(4eIdJ}qouuea=4wz1`*})_%XZ3r>>9I zv*qrUGC$5_h_CRFEcZU#0?^UV?p_7)*lW7nML?H~H}5X1d4*nAZnlH6WrfKQDzR1X z+1IYmgB^}`YGEL;uz&_!Bdq_$#l*g&hutFq(uH@G4m2=u{8k2o*8XUA2kyCi?fjsi zphSz%f_p5#KZWS&)dFZ}_t0l3U#mc`UVrQx6C|${1J%UcT_hU^QD|80x}P>lI!o%x zH9KHNAt%YTwgK0{yL;_$AiFI84vh@kICg=`k4)|ivexkiiH{nZpjsR*)Er*W(9)I% z73%wV-{+X7#%MMn+kWu7*&Ii%#SYkL7l3ypE^cA_7;-&miSeL34ADcsa>IB5_D6#g*tRU^wMU_jj>=`=n*CB)ffuJ@rB`r{@ zWRMAA3u_2m-K3$xuQ^Ky5Ag?s!JSzA5WEL0Io5IOv4#fH@A(k^xRU9tD6(rUY6>mI z3a(dNuU#E_5v%8u87o3d_V*iINS?-I|D_})wrtlIuD6Ant@Yv?<4DLxp4?kjn&!@@f6T|eSIM-o7m?zXnXD_3kD4J z7fmcnNq*;lZoF7?6FQ*UPj_t9>;(;Ob6Ges!ngUMPXcp3kD1%;z=H-FT$+$Pq6Rk~ zy7C?2glbjfriWr=J9SD<9u=uUQ*La2auCAs>`SfwCxp1w0nN*?;{$Jfg?4UYCbpDL zax+`%prXM6b<{BRhw0)`efcEcvD~|1d8r;GW?yY+c8$w&tDkViQ0{zJz+zn8H~u&@ za6Hngr5%w^^*5BKmxE>`kQ5lr4f+{#E#Z za^ic6**!V5WJgqq-lveOmrz{FeekS+0l61*>zPGOl`|VM;Ph+lQw`?85s%p7+{zVA z44Vu?*sbi}MpWEB55*Ib8Y_FsF=L;l%{kQ1PW4xXjI#ao=vTEI_nz|$`F}g`bN4yR zNpq+~w_xsFW7P6Aq+{|X>YeWxYgl5cd-%e9>>IK*TLmv$lkRkCw7%mHf65OLs<`W8 zLhx!&bm>eV2@}@m<9D3L)~B8a<9Oj?|LpD-CS(Gnu346~XDAa>wnQI3SwezA*3qen ziAk8|oizrl0=(DgcZGKxXn{4$68KO~PVGvS8l8x&qoUkf;M)D|?^kPQKlC}G_c*#h zo@G%0M8n-EB%Dx)kZfZqD^`q_jPZSHYOmu{cXw(Ta3{YOp9#|t%5HJ|Sh(?Yb5#G~ zw*$?=Y+NJ5vIhwN=3nA58u@I|Chxb=<-8AJmWVLt;r_3|?()g@AAxiAjdR3OI%ovV znrW&X2U$&j&Cj1_N63?h&iEq~qF{OT2YZV9?Zf-dy9p3qKKu8do%x z8UsT(RSldlJRROp0dmVcil7Mpf4p*WnV@ySZ3RFPwS36l+ze-?c)y_`()NJo+Obia=<-E_Jl&G`;HFeqqZw8;=m^F12fySBKi zHaZ`rtHMx^`*0|-T6Db71$u8T)-e-xFVVu&N+LeXgt+>jAMWZJ`Lf@=`*4^j=faf$ zC(yH&bUoziVxFu&Nzg3QLvF*m)x6vFAV^*Th3o0j)t>I3vmTMyY78KwlYF(fT92tK zux}R5*Dbhfdyp&Om&U~r0CRFxg}i%SxA2xK&96`u1Aq$q=6fS^ zn@$%>m#;|JfzXRO?QlWf>-M^T7$rxOz`-QgV}FD_VJ#iojW3|M_-wE)T~6(jn6AdG zs7jsALwLR9^*weOX?9BHvysv{`@J5-lkK2{zw9TRU|3Sdds&8 zpW~}r*xX!8SNMe;2f${40oBkvV7W{1iH$1m-w4c^ymPAeq}SgyxnnhVrF zi+wM#*gb+bnDF}6x3x7pTz_y1P&V*fA?f*xn^t7E^qMSPlK8L<4)g0 z51DLt1o(N@$tTm nr^-1i|06x4>_orJ}CYoGjPS?f=RPc>PE|5?EOYjnelf*ngj z{)4=gy8Qfox21`NS(VpzPwwFS5w_dv{kCmaeLT9}ClVls==b@9;@M9V@=^&Vb{qTK zl=%qqb`&40c2esW>I*e_f6ZS;glOCe3n;o_a2L~cVVVtxIxL3)S!cI_jox*ywpT4M zYVY8MZ*6Z~R7js>JPZY92Ar>b42QV#7Nqyu zQCFDlOBL_j_D&UZQ5xdp-oXv7r@3oe^1OTCLGXb7#6a8c!n`3bi_Bd~u7}j9G9&NVL$CE(6nc<3sjo*u>UmfHCA_Op_{}dY z41ZPs>~~%b-X6Kv;tqe?3Ve*bc77Fkxng=n(mq-1W2Si0`y>GHa;sHPBpi>3roX|% ztQljjmv2##!v$ws#RjFq?uzpExbCPFUoBrC68FD7mGPj3o?kbOHHc z&6QMdwDMl1mFPlwTe*`gZbA#JJP0@7Wx`|&LHnsJU`W+I z<(5IJiMSj&Ke76PTB=j~AK}gD7y#!c_N}`l-IFj*hcZID=^*38Nre7es}*u0Lhv15m?F zN}r>iq@Hc`q)Frq(o#jm2rS@+!&e9hF7OLm{Y-{*9y?g3>f?v%x8m-wjQYwa+nn&4+khs$K)X28y65CVBMQ$P)_DNDS`xK> z!3ZjMZnYfJ*USp}`J^&R5x>9c-K#Wt1DP2+$EV#2p)dXvp}M*NfYI-ADkEb)6yHuN z(}vy3`&{oKwK}#mmKllMFF|F~Bt&$M^+hxgk|h^p6B-#nU3L{IZFKq{1XLPS+DR z_%e_25el4{_yhz#tD(U@bZa!VMaqK{wbltm*3@S2NP=6{>valOx|?#Xk23Dtn}vJa z7kRa)r2Zkdub}u!0D>AUNRPn>RX&-HPwdJ>oaR$?e6)MxPoc5yXS+CZc@_JKiluLO z^j0HZ(~8wz{BF1pHpAW|f&JB_SDW3Ah#B1>2!vtL&O;)&l7%k95l{E0>B(qm$>^RI z==9EYMy({j=4)yzr+=NZ5~vp08qfv!@b^GeY@*&y;(Y9z-k~8c@ZYlHMEt2g0%F{g zb5epBN7{SX4!k9E2ie0it;y6W@Ce;k3LNKPhi{3n`Yp0sOV`(QsN*N6ju=(AYmQ(D zv`LN>eQz$Wq7rGYhxETy!ntvH0szhi1Ol>-gJyNL!`KmO8NTWCY))MXH6PRzaX*MV z?fio&$GglBK5kC7e`C8J616Tm|(jYBe(p}%+J?}Yxe{L_gTdxgkt><~>9CM8O{@oD*&*-l2Egug8 zM>mE|7bw+-`VR<)ho!jXZu8j;9`BAt(4VOB06N8buI(aVC@F?b0BSfEc9YAgbIAC* zwEMXw*|X}3bH@J5`oc|J)~<{V<$#|>TOq*BP5)?D5HY_*wp^`5C!C`B6YgDXi0)ER z<7p_SrT&Xd7i?z@7QaE+F%eAI&m$x1z)$P4pY-L01F|LH<4efhD^?IL(#zc8;ikE& z_^vuoA_{0Tw8Fw3Vk32>&|N*f*$E z6K9xcmE3J>ZM%vp`pqWI+@20u+rT!)*|yklVf9G;Q)Xfo$(X05#V6mof@8b4@d)yC z@6ywcmazG~I@(frAg^NL%p&b`W8r#6{wB<1J*1d$@& z9yv6%qg;1ta3aTjtIxC?uVwxF_GY_^S)zQMyGKKI&28i0(gzo==uE@SYod66DwXbu zcR*j;8Oy=HOTh6T|NEI&U_0eSGYAEsceSIZC6IDv`CKs{f3C zvG03mC^R^o1Y4;B^DyNSc{+PJG|SCCF3(550`ijF>t9K4$972lL{;|Zr8~i~#;#c$ zw9HN4m7JDCDMS1M&5?QBEl61*52DtugAnW88^!oMc$4}>>t>*6T(ijntJ z_oe>sA3i7aYfOqtVMtk8q4D5gWf!=!Ywxb_f_32{D|2NA*wjF9wk4RU7Db$GwGg-q zY}fcSH*akfQ#|s>*Kg1}6&N=YMwJxiE_d+TuZB{b4)tZWvoN+<{V6_%u!vN*v(HQ) zJW}(Id*BH7udhfqGvluPB*F_c!P5iV|N2thJ=hK~)JDyBV_ z(gSI_dpY8G%n$B`3+j}+Fbw5jqw9aO+~`Eso%}I(oVF*+vYV>K1ebr!;Z#`lGzLP9 zQ25Tn&?_KdQ6jV{diUK<{;KE&3jATKnF|wesXt3 z9~XeO=3v}+ysT=Hvm1QzAQOX!jMO9iAHI#f=VG4Y^yv%~GKg$VPyckdGGfm7FBYYE%yPiF&re3_E&Fr}F2)++Uk<4<1V z3vV9)yq}}}yUo{OqTurEIOyF4AN(&kQOKd_{b)t~0R5Qw#2gHx##-tt`z<~`;!5z_ zi$|;8SKFY7&3wnjIToQfDIHZHi1^Hg3Yn4US@iesD+~>e#*rh6WumoX>=(qa-4?65 zA=D)+`*COx@<~sG(yLHGYAX*94_F4RgyVr!&Bj zh#i%2S$A)N_t(TFQzsRIrQs=`55@%apUg#5l&cAs>&MNtZqUP z>WMiaa1Cp^@!n1AAY!tkm{9h6eZG=EO%HcE?ZqC=@?&uM5q^IGAUhEH!B{88dff?R z|N8FxBsnVI*-&Acpv<#|@N4k;hEaD01qZv$?T>)3CNrOb?=Geu62}HJuJt{w zh#!`dE~|qp@S*LwY(oMbn>c4%wk5;;`|4lSEiUWN0TjXpaJ9pp5iq}h$-CaRY=+81 zE84LnOn73xx_b(&hlJGBOL%sKE;Kc2xX9>!Za9=%x@#MVHNn*B(WBmJm$e7(E)9Qe zV(sDxGnj(eP~J<0i%;-|2G0ZXZl&r2d=PSBrNzZ1q`lC$1gA43aPjA0fu$C)7JT?{ z_Tz|tfmUfkHf00z#l@m#SN>}%s9IF1q#-7mL5dsi9-AONJuf}SlW)?|`9QoBBeZ;L zjmxnWI6!cEb`f@G)8nFwI)aHvTy7v=hcC-vgC9n8e6a6=K85vL?F58q4t=v0R9Bmv zV}|4!Yhz;!hC!8e335n-9>ibjKy`+PhQ6>EEq>RphDIY0F>6x;@)oBgRv5LwaHVx7v?O*5{VfgMZqUZ>(Vq0UGDY^sczx zh6Gr*k{y3k$ga&aafi*VKi7&sV9qMJyFOLr*zx>nBDbXu^aS3(cp(f#zo!B zMH?r~H5g`I`7y>PIZ=k88+~K`A5qhfYh!bPVqffrBz{U?mQ03~)?OgCu zp(6?7#vA#iUU`s*UXuT%*~$~$k#ix=Z8$}WBL9!<#i>X%O7y>IFP*xB3|-^*OGR~7 zwS5(btQ^ySR!LJw!XHW^nS%|Nup;Si?jop+Q@Wi09g_h}x5o9*QZJkZ#dRN{_`5vf zzKHmX2&WcWsz9{HlAyX=%^luor$$lP-`tZ^d^2fp5*VS7V0#@TXw=nhbzh$Ha41&k z^XKZKZ)YY$IrpCLuO9!s7cTPY$K@MqYo!*+eR*n;-FcPXJ=X%OMEtLS;1JKXJN0N- zwL{$=wz!x9VFb1WQ6d9y|B#{OcF_~-K-j*+c;KaU%)waJgW7=8_9BwLV z-!zpdWs2NBUJ0VKFYv+~M3;Lssu0(7R7!&Qy!7o{@cL}m0qq41*LD5xrXtfD+yW*k zjt<#me#O0+Ou061XHj2eg<^S)i*?g{^D?1$Y;T)fT>-%0^1?BOZEuPTe=|j$dv6(9 zrN1puFu6Ov?)*bUQRK$nEjms)84k`Z%z^BpwPy@ue=tsSQCR+jM{fLC^ES`9OkW#v zUPNkMNvYQH?W=X=i~Us_&=5YfRQypMGmYLbrT_8;#G0IKqGAzW0AHJ(lf@dWT%a!J z2K z5ECy~vjcn<2f85R4VMT&yqE#vCFu9Q%sVOvmw=g>SDmfUvl3^MP||4%rVYBZcE?~c-`s~@Wjh8$o zTE%QQh=go~cy6PfgmPHkgy4)U9UZw|{mJ2CSqm1zd77TtfYqqQ`$-PSiKOBAW6uHaQsM2g`^96sxe?Jz4jpXPOI+5v^n1D&IO!Bk2WRIUxk7L zuKCa}-{TH~3jaBHi;cBj);g`kakN8}B$KqXO^L-XEVLN*mj{!M(}Di}pmP>5t^)i9 zi<(S=p!J_hYyBtYe;|b+jOJ4A3Y%Nhld$02LMN*w9Z_hgS8EqBZxE`ZC-0 zU*Fo?h5i+AwA}U8_^SD25d|1nBExN}%RlDszz@mFk1gE$BXcXGa|{<5W10V5IOvXb=+{ zOTKQwu=%(BHga6_2E&Ce%Ao?DH;SHp8LeYuV^0I|XL*19`Yp>0Da`i2CaOR$1`13V zj{}K;ASEnB-sKoQU?%Y7C8aPfY&*wf7X@?h-BapIYP zP8<9e@DAV;!Gjx1OAlY*L{)$XQI#doBi4RxkNrH{BOOEGAM0ok3w8R zPB~jRI#(Y`jLjAoOwXH=!11^U1_)5}6;hmF(YWpHkIV%UT>33JL;18*TolnLh-UKT zb?EIw`^>>XnBJx5uY%Czwq-Zer*_>9z1d=?X-V?>h25IyMajx{45bB zf$ei+KPY{n3$C4WLO(&EZXt$GeJBB7^oy(>k^m!t{=OrL*hRWO+~sj_PPKToLwG8Hf}Xu z(-l5?)iHao9mAU=Ij-aJkjk~k=O^Y+03SN60?QEq8Nw%9*Ceb*n_b|!zKK4d&=t)A zGaWc6y?Y166D9E~<<2Y?_(gk*>L9dx}QIP>ecB%1N$HbO_k`W$x^He&k%PH01$Q;e?5|oT&e`F*z@*hSVSdb zqzB4E={~f3?zOyQ^L?|8zWJ)BCCLwR#~YT z15)az`xI1xzP>)rDI6^9KDd0IY^cAO0)&vfoya9vK^V!TV*KE3P z0ay~xV`-yubX2WCqrz%>nBuP{muKrr>Fe9OA4w!`Vx-G&ytLZ$j9z`EwFmIG+}w_V zoZcvQ6(^}&qD3}T3AfAXMnzBZlgsfBk*mW6b9N{%_76G{CRlBnO2if8?mL6b-3E?9 z%AYiVc^H!L*!-@vA;psv+_9U4ZE?y(ltjUZD-v|u14=J^iPkCGB`zaF!<)_WIfeOc zB>ZjGKt>A+3f()d<*TY%xMhSk14Q=hw~`W)Ca+Cg>-6LXOez%P*7!Vy3&j1My2YM7 z`?-Cqokz|VQ$Y6Puf%dDK{i1?zC1fqOh0zUm!e3%y5AEo5Uxo|_bg^X zLw`dz%PfmUtpGq_!Stw%#@@t+-<)>hK@wjH0^Nv7D7KZqk?FEmu%XBe0=3=FKC}lA zT<%3rx!=8eS8a&q)}62U`S)JbmM=WmU3`%H^6SfeO10syW1Qx{Cg^DD!y$HrrR33e zOO`)zNN@vR{4+XdW?e%}#rG%+>?IYk{Uk}MXTkUT(1Rn_<=E)(}VMG-D85P#|)F89Gk>wbA2 z1#2_365mvtlDC1L8!mE*=cYF}LfhKfXz?m0hgZ)*)5`O24R*HmvAWID-Ug?+k5^p= z9ZgMQCaSjy>eklZlW&&iR%#A#cheytb6miI&1j*E2Wmi=_Xa2PkQfQN{?ZYb`ZRvN z@pBWxtIdWBn}W+Y$s2LJ|HG*uL9cP3<3xy(-wdhNDXO%wPESt3vdJj!XODhc6>u#TKZ4D zQTty88OR0z!_ZN`j1a_@1KbAsW5COda$u@cThnI!se#1B{i;%9>E>2DzVPzou+azOah!MJivYYcAf(K2gW9UZp7 zDxfxyR&WbBpY6dj2F~glXp+Z#)1;uJ-1&>kp_(5BmXE7Etylhy|Cw$U5|TGJvFKM*)ic)pma4gJHQ}=lfeWT9k6mZr+|ZG))~z!?0BaD#w#NG zgOPnopIOzK&5kuH2{Ib9`dW4uKoBQo^a5 zu$l?BL%I1OBNy1$FJdrJ=!h=tpafAgZn=s_9*gtyVD<(CetVl_Vm2ls!nXpC6R!pT zTJe3bS@JolB+7NW!b=hMxm=4Cq2q0_h ze?HWvBMVs+m*64^?&V4Vp4yVZ#F>^9sm3w)IT8LnPh+#>5x6d6fIS0 zh7FVyHu^V8t{49Yf#c4o8WEU{dqYQYf!Ct|z&ZV&x zOtHqX**%2QKOV3fa<|^V%i1~>#o82Gl%nTimynHh5lz4HwPFgKJws}{JJrD^lz3FG ztqF0lWVYNr5uXBCe>D!_nEw$&KbS?cZ`Qlc?2bKZ(fx`O6ZmVK7w#D%S*nB>VJ$zT zhUD*Rncm8JMc$^bn3QNO5r1lyy5VUt;96D58`@Nod(Lt8mV|_3vytYVpK{l(o1IUI_V}``;3>07dKs9ZrB@;@S#i89RBI0YZr*4> zd499@Arxo6@kR89fT`#1NF~)7&(g`0V3VwGxW3!>CryLzCi7aGo$RH8Z6!K2wRczN z-8-F{9~J`k)!GiI=LC0B`7KUBJrWX(BLU$7^%Ky!TzkKi97TjObSAj$W4&GoSIoER z(pIkm0bmb<(I6i&B>Z44}|KZA_qZ20gY8vU~4HW!{KB5;`|hFIPNozO)&YS zFu;iFxqCH0zL5Q}>|-o>S%(8zJJ!;o;4H1l%?+BX1Uvo=G8SH-!QIe{SA0?-md_tZmByyQSm!80)=KCDD#CGMRs1DnvMp}JUPXmyt8b+^c(s5HwtFI z6B~IskSja3dZ1o0+4i-rok=oBa|1H ziDlOQPCC6-o2A#p# zYZ64fcDaiMG(!rst=#86YOtyYRj}b26}?5Z`CdN={}49Jy)h+!Si85n3&{il1}`Ee zsy80gxi+lZRKdoq{7IV)iJmBRB^fx&n-QUnKJQ^g#7HEG^-u8OrnvvpQuv+&2ALbH zPZZVcK{bDEnxYciyx7xjb2e3MyBP{!H_-L?@9$;Zcacowqty{nAPtAJSx;194 z2CLXfdOd(+?LC_ zn8!-h(`UDbzD-ImxWQ>@kgy*w_om{)25m8x7$Y0 zD~WFalGgIunv0>KxQ(<=c0!C=#NmLvPuN4)7l-ZT)YM3mo3ENxA|(I9vH3hp(P@Jw zzGqT^;^v!DW}A$Venu2A*)gFwEFE~uPa5a8o!qHq7=EB`z~rODVj9?=VHR+H1zffE zR6o}bohaRuPjkHAMrxco;eCylTU3}IPC;w*XKSl{bKD@`_Ne3TUEl>nISVl0cHn0_J16Qk=v0Aq$Kd$Pi_u4r?4_HX_h;TL)^7|;2Nat;4VIh z!gvl$0SP5u=hg1&8*5#y(dNTtkWTw!O@^z%b)+7SI`fNH-OZ72`VI2Q2?;OnQW@cc zEbRDbd+w{H1ql%m*Y0Wj>)zy#!NF!zh0e;#5~&*P8aP>LeqH#Tn<^u0dbUg(pYrpp zt*v_!xMie^mUqoE=#%)JT{gXipsV8`@DzyO3wEqG(?93s-Pqt`d*n7o=Nw#ziuop| zpimJMofPFJ6QX_hUdx&A9Rp%TNGKPqg%%RD$kWo&-~-Bf{O%QbOr70RW;LDu&wHUg z+K&O&*hYr}cz;z^E2wL(ukPz)+bc>Dv4s@sm0QBnlWm}$ubKyT@JTWG1_dL2#GGVv zKBr$`;97#m;=0z)^!`8D$r@Aj@iONaOzqafsh8_xrR=fr2?lXZb6>4+jj3-g+C94m zyf5#QPdKFj6IdANEJH7&ITvLI*naKuNL!S7Y*3;wS~v;O{k|Kl`c8RVu36PTmK~r& z!J-dCgnI!W-*~>hZS|Q&%FsY*7MT8_A3ngJuS_mQH~&2wey~|tF;Iu#T;33?Iaq($ zVCw!(C#r}7KlW^F`61ZCl{`G^2UZ4ok`?>2Y-4*R!_=xI^G5pUv2Ikwi+zv5zJGe!}UkQ_Llm#2DMU?YsR4o=*f z#Ke5B7!^=jAa9D}j(&-Xc9 zO;Ojw4fn0|z~+0+KzCsD6mdQZnpF@wRxOSB_%TB!!)hZtj7oS%{2LiGf0u2lINKy7 zB+AW(CHD`@7UAbsF-8Aaf%3gR6onv`O zc@&6W>a9tVhq}CF`Tgrajs|iFfyy>-Tj2jfZwnoA_bDffUlV*jwD03~Pio-?KYcg2 zOj&N0D~t7NLiD8x4m3rfZwVCJL%Z67P?hNgso)18yuLrbnpfQ6b1MT0AAq9mk{^g( z9n|M~aefhByidVCJH!{&=wVTV_KEt&-pT;SzQ@~(b^j%pDgzFEXm z^0O(|X}RNr*+6Q&+BdV+JZp?$sf5JDd;eTF|Bcq;@-peypRMvV2>#jcvt8;fGat7+ z4=8UDp^#@T?_F@H0*~bfnb5m#7sq5z1Mu@+majruOrdt&zxI_!%F4>H2hPSCwRHu* z(q=Nz3JVQ7a~f@z`XIG7@>Q4Dv*|o}Q^a9?H1KIat}uyFQRs3gL<*NK$<&cPNS@Ad zOkI$P{i;PHk83B88mW-LYdyJY zzq|AcI32AynSt(H@8XztBd%utd8f9$(2nT@W2|14%UC}H+#;`sC$v)Cx2tGvO?=at zU-`+QTsA(A_sYkyD%9#DPNm*U8)1GZQW?WYY#nPj;%{S!TeCq5nr|4#Js$H z5G;PTA3p%}f2U=WM`+`a;Q;28_I&C|3wI0mpK|l1_fi_Tgb+vxx0_Y$#q6donkX_p zX2FK}X`tJlcrO_+R%$%XPyGD+qUNJc_SY)R##UprEya*|`HZZ8zqj142SCrc%MC`k zBx0i3>$g}&UXVVU{eGW1@$CGtMG}uk)$kh%thxlJPNM@t)o7TsX(xCH+A?~7hUT=( z@VZ_6@QgRtj=JB~Buu%YiDP^J7Q?{!tf%&liIyc7ss4$Pw(j2Dpchs?d+aYZ>kbK# z*emCMhT}KGZ#+i^<-#QqI^6m+J~h_N(fl9N`RJ7^28g zXt1TX1PVu-3UTL(6Ie0$A#%yExrBy_sS#dp5IZxA`{S%0I!ZkCtE1mDo#j7qYXRr@ zWrZc^&cQugclPz!_2^rFi*y@=-3GT{Mm%V#*ka5*RH!@Gc=5F4F6`JtMJg({yqSWl z-xMrO&|{F}iLZX$tJ+-(@DJ!YGLzi@qozidmqbWLMD*voZ99N}>h}6&ywT9S5Pph4 z<=cIo=qY4Nsfd^XHgfm&TswzewbQA?OMjdN9rirCS`6`y1>$Mv)8wf-mx{NO0X*Vq z-d)(%P>1N3I`j;gYrtsG-Oa5z!`D-%>Se^2(a}-3yp9g{1}kN+&uN zu2tVdkuCf!1dbD=tr!{^!kJP{t2~ZP;`jh`qEe$G7~Y*!sWI0k}Z8jBp%(oO&9##M5PC~ zMpy}``JI3r=8kn>F;(rnSQy`vY9VahjNvKb8Q`1Kn9C9P**R&2$@3vS=Vl?Yqda8%e5JiSs#1iN{ z+TTCU@zqQg<&%pa6{s79ejg|@4AlzzzA&cmIk*2e<1bANI{|WQ02B02A;Z0pD|emB zKLh9hq1S0&5#D1hnL=;Vn?pxOhxhEb+>s8kzd_Vo7-BO5b)$L& zXXRv-IvKb|(FWuQZ#EPHT}|zK+ltyUgH$u6gVf!28xs;fwf%$r;IOc%D)XgJ907Kl z7m~7ed^9xQUTX_wDsZ(lnU8$at#V;VH)|eHcw$3>@*|YGn@*Rm{lr3z*ni1cOBv>6 zid8SE5RGbuFCv1zgUHQ_liW@}4Xczu4C8=i^joeppe1*AcS3gW5Q;|*DdsrbDIIg0 zU;b<d$sY zOaGElo|^S$`z*eFoUX$a2(RvL*^r?UkCu~Yk)qoB{bDmV)#N`%-?fq!cq1~}ag1&} z4cgmLBhZdP(ix90rp9t;d)zw?m^9#EV){fc)q8ZD$mWmd>y$#n{pR;ZbnD-N7$;sU-l3+hIPDX{@S%3G`s3$0mf@a?Z)m*`7(f@$$!MAG zr76Y`Z{);lBQK%nq*aXg0;NVHA_Fb-Vej_E#f6t>jLH7>kBs-4`~(Z-sen~b)m@eZ z8iMt)n)?74u&f{>CN5z!dZ=wjbG^>!kiV05uXusVsJnE%5iNDe7-(Ei9GjXkp%MRK z=vJ!x!C2Uaz?L8Z9TOYG9X#Y``SXU5Udkbdm4_UUF;Z$;*MPjzYN@GJhD^W8rS(5w zz4BRXwi%uw^=hG<>Zjz$4TF7h0n2sB!J4F@frjhCUZ|bibyc*qGkGitE%%B;0kC*N zJl5@pZu7DF_>d-K&ns<*m^`p5Lxzx@6+3kj`8I9!1-i4gxASeNo-~~LeNKMXjF^^r zhzAlbQ6wkYo}(08(+07bXF<*xA-JcpITMfLF&lB7UH>6?A61HWfksE?t?MiR*a6iN%tvk857}&= zJfOWeic}zF(^G_ac%XCo`}tvD99vCQngH3OK}~qZnOZyu`}&Mt_d^T@XZxhGSQxd3yZ`)35pYxD`}qj2xUB*!W6nFInPtT<>_R zvLTy6kYpkSYQ>J<9Fll6!m86)zdi3rK5BSB)$hDZ_MYm{prO9!CAT%H%8I^551MFU z?~LxV&nQi`O0PGfh`+qD(rNmSas0)Pv!anJ*Z*Gn((h`pAjplTep-y}3}in@WnWpq#9_(MVacnNI_#oqo%^6hKcYC09OtvHRZ*sIma~$1StZ2;&{K+(4TN? z?Z6csac$i{Osn)Y#m6?N!baTlNk5~_H&sQF1%{Jf95rWTo0(d*{P-v!Ota?<)0^ua zQ`1W8&2fZ2bbh7jPVP~5Y^?VYfKES=vZZn{LBDJAPDaObd&MX?@^BX88O(<=O~a%W|2m46X!@MOjI$3q@b1G z?dTK7^DsWA<(W@(EdlNizqsjyqUdhpovmx_f{VzEn^I(bq{9AmDR)obboOu8eZeDH zMj`|Tr(-0M;&!Tdx*~OS^uNoBHt&Wqd<5#MDG1jfl!}Lofp4f~7s z9~^2Xjk%7GPgXqg5x#uWy?rWuBH4TIN+^qTDu;9QQvvXHUc8PK5<9kTQg#x@HYEuP zQuNjzP&JRed+bW-jZRC@Z<)oRUElqNGx{G(?>64w;NW0FL&4e}f*@K^2S|%o{}>V? z;I{mG*g{(_#iw=P%?>fBzg>7qmX>@coXl4&y~Ef-}S6k?BtP8-$IwM_q21KbMs3Dp`Ne zkGM|qw`(L%cj<3yh)Cg)cSW%}N9GvnJrIc{xJ9xBG zC9P%@!ftP16m2z8`OscYtL$IGwRPCWlqpWBAr{VaWHl3H<>QT}Up zL1+iTN=W@O{9Gf`yVSVox`PNXpilghrxIB7&F<2mbq61SM^+=kMdC>^cECrs+o&#& zo0;0&d+bVX;s5K?R%5uxS*1dQFaQ-dCJP)@pUuPeu*9MHUWoZ^Yd;NdikivUZ{o=; z700JYZ}hV>H_Ab)t;xgb{nJWFK0dJHO#LTIL ztvx%RsLIcGKk3X7zDW6THwQ5m7@fA3F z&wS2YZxa(flaRbAOUm~0Q)HrUg?yd*SMB^AAroxz+_$5sadXs<2cVO$wp-{A=@n5x zj+VWJk!|7{NzS%-&`|Ta7>@=SbUv{f&eLYycUT1u6>bR5Y3)e6ArBEc z<)C>noOXM$+@}gOI7Ax(oo1Ocg^xrks*yBeNn&^p%;u|9EG7y~C7lcn#_GFQjo+Ru zBt`WZwJ*Af1d7RSPSxf4qAi54AxgfGe{4&Ciba9ZviMcIJuH!sgv5PoM$F1;)B75h z;81Q+=8G@!*&cMrxL@f0;^}91jzf$PoJ~}Q+43oW*Vam1dR8AAwR7V6Vw+MtrlXt9 z^q{eK-gb`FUF7{`66;z!c88AJ{tuWEuNUKG=9@v@&XYpjkst2nufH2};`kA2#n5z< zTtj>UojJX3lA4-D#|#|^8U;M2zPlb$BgIBWsy%l4?tlIBLz%es&WkiA=}rcS)sA3e z4n{|Ek{_XBIY2eq-O^&zl6~Xqc`_hJNBSv7wB}r?_qo_Oe|z@(eeey$j~9Acce3OZXc|TI|3mY2z+1lb1rt1Q?U?lo<-72DG}?+*^Fhe z>*~>Z$tDQC?H>J(r1OzlV2QpTmU`5To2>g-D*W@1fKJ~PPwT4sZriPqIr9I;8?3sIlqRd|$LdvXODM~* zg17YK^t4E`+HmW4(|?98Z*C&I7DgQ`#y_6fe2I;XU5?PfUy0BDNcu~g(`6zqz7#lQ zjK8_dVIcjeK4DuqmMq!f!(5z)`zZ)Y3a2aUPZNa*{lI~gENtP@z9bMF;WWK;SBBi^ z{D#DEJ=Mczv+wMhWoa6ldM-Gb*}dF%wp)XKzQmTlz2B84h;~QxGf9kz2+dLaD?-R! zN{Vr4@9nkv-H2vSIrybt1nvjL3ui{k^^*-oWgoim!98t0nl`Wg2!@u3yF)vj=;_W+ zl-JSmPAkJi*-+Tas|#OPHNFuO6Thb-qoUHOdudPOsSvBnu4^&!X4bY8r%0#n?|a^} z^K(=ztJOZ&ia52OqEgiSys@96tZZmX(13d^{739^@W&54384wWnBF7B4i^*0fbae^ z^XbSe`kntTIDf>;EiNy=5vn`@D>3w~`uh4zODRMZbySmV1X|n2?{l*e6M}B^Ym_#| zt)COIb%C#lZr(KDq~un66z_jln}JKl!4ynHMg{?*Ne--#EOrku4*EcA`;_6MI@(GL<0#sb2l?OcY~4 zc>o6o7uZdO)ar!>lgf}|WLPntX_O(yc&ll?sq;y~@m^UWd^ntVw`gcKZ_h;A?VBnY zC>a}n8EVOQjF{s^8km`xS?U#~e$#*Pg3}|7IrKwchdE`H+rkc5s1OJc(nhnWV<28+ z;N4`iPA)+n7=pqkc4v3@qoh$*a5TD)yDq*~_}gJCyV+*{!u3KgFE3CMRqO0T7ieIJ z1%(9%5Q<}LPE>D z;79%uef}T6kU?4yOvJfD3a$+Bw%5OeOOx_QRB0??i!+gHih41zm{=NczDJ;G4cc9+ zV*h(PlKW5tqe3NmMV2Z-j0bHKX4m}YGRfYA`pb)wOd>M`Z3b%P2{#z zIfv?z-b~UyjirLBCK)V;ZZA38d}ukT5R`;jH6L%s^(XhW|FWjCdWCT77?tT?DHi=) zoNe~64Hf!#HhomE7l#l5FYz;puM*S1uSv7#f`(}7-fYYSlVk1VPmv!b>y3@1Fxg`Z zS^2Vh`*J905lBUbyHXLZ#0+^P`=s16HeC!fDtWzoO$MveG-8D0_4BBE)x@C>uRcJ* zcTyERyj1v5H1tRB=c0>V9ZNkwp*LR0?NEs!b=g1gO076jbANpZYi7RTJq$u*SISeQ zH9x(s7rSlxfb;a}Ny4h#i)uzM2&UxSI0TF`7ahjkT!GZw{CuXL8(v`#9r#tZaPhDG zyIiibC%d6YED<9`(cmWZR_Jrk=}WnRB!}|uw=P7~Il4oqXESFnB4mG-e5`pU#X*Ee zJv)`gR3bYOe?jx^`DO3ZanJWN@;USJ7V|}PiYHXiwqO^sBW6eq*_~R-@RZGZy`Mfg zVBWZ5o0}?|<=J&PCzZw8Z(4e+@ack?814YH%Pw-Sz^k#dnZ#n*rQx-LKN&gdyFx~uV_6&Hmw zyx@@_9}Qozu?6P{9TAi*Ru{o0j-)#RAnpW%>7|^z>z==h+=OR4)?S>^@#`D#-38Y* z@Z>i!F}1uFWMnT+68Fc=7_G#SOw8Zq;iVF9<+>xpgYw@J8eOMb>>NrnzQn(q$S;JEeY&s-eMQ#^}l{kYl&eYu@X%d;9pHJ}h$?m~l^RDp7G&d?$fiSXj6w3WRbGsoe7hhv+%pm_WqjWOab(Eod4MiuxNUfxYeIBudHes#*5h*!cSO>t|>oyxdxT5$_Z#RTuB08^<0P z=WJ`VM*!$rp8>`q>I2!XN!&R;*F;8u?@n;_AvTgvZ&DAX0+x z8!yWL;91SE>sFRH@0#A2b8Tiqv#K8xVq$_*jxNq1!&_f}ZBBc-CgYjk3dybcmW>{M z>67luVzEROh?AMqlVcP7tFU1K>%W7)QXLI^?3dU?JR8&>Xg_=A$9E^A3ANQS5iN3F z=Drdk0r7XC7LRW~>v+Q{*eoU4GWu$Em|fG8mBD0hd6qo~gKm((AXHA>VEW?x-t&*~ zBAchnxi6$5CQG+@U2#vY`!h*TyFa<#K$~hn?oCGM!ycRaJZ8_WG_avT|E>1v`P1tr z|F&cXmj4wuHGhq7IB+ogldIIy71l}*3C|YYqt+@pvIo1nD#Qy--(Q8wkyBw5LH5M6ks4-Q}ZhHrnrxyOJ|ZFyIL38w{qG8L5tB=9?y1Rr zvStJu2`EoD8%AzpBW8%6(~jlk4o zlb{Fv%M?d$Z*fjb^%_#NjgQN;U`$O9*(?~k)?F^@GL!GE?XJ`w<7UfJaD0%qv$KO5 zD1Zmy)$)&tiH%#k`*C7&?h8WR&<~yTFCtt{9r9_BO-*RQJ`7q8tkj=;`Buh)^8+xj zo+61QhE&(r*2W99R6@U}JPe2QlUFV-j0jqKdZf|cDf(ju5*b22ORM_)HJAL1tnBQ4 zmxi2w&Gchx2=7idW0OVx? z^CpeVLGn8_R*De%&VQWPj_tCHVro*7lGod}(a`GiUA|%Zed;uBCeO*5+}_>Y-{P)G z!kN7O-!zdCcsaRuvesBgoq1XVou1`C;kq@SKgFUNB|2%UQpY!$1bhUyQ zQx zOoG065F6^(U8w<90Ik2{XbBtzFG^vKZoszkH=hqdW3 zv2=cDHkgZ#;I&&GUEf@MqZ!FiB^}*G<31X@WbaH_$_6Q`T@ddQl0km^w!qeIZ#QWO z?R#M5CvWdRxphD6aBIs8X=-dN8^1HOF{p|Z)pR<#Pvc2WOqJWQnrV;7k!vPT6vR*> zHqmP1a^N#TAlkn>BpRKBQowuXBFDHfM-8zv7Q;Umh_Xn;q&irxNhT9^PfyQ+H$r@31$nzUql27FZ`Uf8UgPJoXB_+n;zJLJm?`b88vY;g; zNhlZ3WiT=N89Kzk%Hz^z6TQY*T5L9yFj;emNAuXY`RBvl$F2@dJ`%^ynAh*60&S|b zcot|X`f`cfE`veZ70M3(CNR+u802yRbM5l-ah%7;O9FBUf<;!-!l;Z1Q26@zuyizF7lIc=fC1xvaoWv z{my}s`~T7P7C=?EUD)p?ge{_MKvGfxQIYNj1w{z~>F$)4E&=IOBn1Q{q`Ol}Kw3Z~ z1?ldFZ}GnGH|NZmIWy0Uj{3mnANRfPwXW-TUB9l>Awxk%NqG%bnWBA(Cm#4WZkzDFR%7zj5BETkp~7s~d@rup zL^g#h0aM(2(%6YVi9Tu!#Y@>ctA7U}$_kP)O=@n-r!86JvhnP;XMO`_PJ@l`iRMVX z-@zwxT=UCgB)lI6%2DaAe2p6e%r3qHs8zT)S_=u=*Whfys7L=poTgA;X{xx)lh zRPFJ<3xu@I$17mL!|w;2#C4h!JQ(qEBM?&_m7~B$WNk;F2hQXSTWAO#TE?mfV4#1$ z`^2uq&1$Ts{LMHw)}t&l_2}BJFtgH?Cx92Bepq-bC7yRZv54MXn{P+N!E5r&m3lm|B2suwYXq=-yMpgpG~uxOzaYjAnod2f;t}CWTDq z6de<>I4Ga%gzYXTYI{!nj%!6WfA6Y|IAi$IL>nFN7ZeU>E6SAQ7S?Tl2l@PHp@vSD z=iT#i#s6-GRZsN1oL)E`upAmFC=HK}I{hwUaSrcL#X#6VLIo!BYi7m?^k_~__}AG- zI7717n3-i`jJ&ulZZdNsA|^IAHois03XSY=gVGYaon0Jg(BdkKD9N9lpCQl>^oymDc^XJZel2*` z#h9cUY`d3jAbX%JE{-waNf~L}AX*_Np?F}X8U5dUQo2ciad9QmSw=(ikZup;rv8@}jk0&Mi;a-x7!;FrQxIv7)=LoV!33Qd#MA zdMjV+7MSP1!GRfxXkE68cS-NG1eKs7S27Ews5BF?y|MAZ%CH3t^lnQ?RVV2et1BtZ zqro9BE6c>;b=g|Kq1wXr;E>!RlRj-G!ochs|M!)mLFkId)f)L;MzJAqTQIS&UH@KB zija_h`^-z6m9{lWn4B;afsSGfCMO~k-c2OC!e0O_6Y`ZR-<(3UwBCg=b?)$r4KI?} z)(H)9n}j3)0IqL&6uLi!c+|*a3_QRfrLw3yLV^MuG$s@EGp9m$kF}vekl|@ zLy3#N$U14@iNS9Cz{#f@XsSY!CL_Pb!We5yaN9J z4uLa`cW}-gEz?{s;CM4r-0*2adJm9l{cIs6yxH2``tRSr%q)|qpYSlqZT@@>Az=Zt z5Q(lgs>XIbrm)aD-S{Rsmqma2vqKVX=jDIj6dylH&R5Jj7|eKCd0v+O_H+T^ol?0` z?XaxSLmq^{_&A0_p%7@!3uEmk`AQ_!_V<(KT(lxR$`JYYyQ0@}(io$PQ-M-=0nzR1 z!oBys>^7-}@)FQ$7x?_cueCTnKdUL;4w(q43j)AK8pD zioggn*_+va@_=VU^{27c9t<{Rt_9}_ia&R(#HDS;2@DEnW@36FD_fu>f**wIxAm`6 zQ7<+1a}*jWbaj2ci?`B*wBgd$!ACPv^F}iioqs_jm%?Xn&7`7u1x3j$7bZShDn^OZ z+&EmD3_y{yxdvWb48I)R`zPy)R-TrY)*Z{tV>X3iV?h2r-dzNapJPsOX-%x9Q*KZYwHS7;;2vBitMwPwRydkbC-3- zEggk!L^l7h9;;bjcXFi`a=uId;O*p}eJo!TC1u8oD!YteEgiQvE5FQAJbAa~8Zg2s zgp|w8mE@xlmJEuOT}@p_0HfyrL_l_*~0$nXl>nI+Cx2rnEw6> z2r4J*J-ig`1_$3i0=Fvg^QF!n?k7xZWey0gp6WaJ97 zFX&#H>lW!$S`||BI%jLujwClID5IN>Muvxvpt zUEs8~upyy&ThqJoa!Fq21pDYw2}AcF6cbaPx%XSd)>?nI42t7RyL)&jDcO_keKOjI zsm#xmM$?7Ru|P57QnR(V0b8Q3YRwNhY0CM@FKa&v+mBRArWdP~X2J#MyMI4GH0{R= zRp+JF>vP6lhxNiS&|G)f-B5M+sMktUb!AF*>bQ#(H++iOT@rkQ$MW}9ex;pCf%h>YmS5jm=)PS&}g>zN`9m*6pZHH-w6O2=vi+u0)Z+k@eipQ!-)pWXl zJi=*g{JeouhKGk2{$xX2p7HX5bIkeVG(dI5Z-gERd=S8pxi)n2?mI=3T?U4q=;rkt zFJ5`NtwjHMZjH0dCvq!0I#$Gpt;p`n@61AJl!q7xhm%X&a-It3UeAexPfz7iU(xsp zc3%VsH=+Hw&@w=HK4=tx*~M)&2IJCrlmh=4y`Lzw9b}A1+by|kHA|R#%@d4^6Fyo` zxi2P7!7iasdH~ESaP&XcD0}refjLtkiNLSF#k`GFAcVd$<)qMGA(~HYQ#&a3^86Vs z<`uC86tj3`?@_^Nn6zaphvnz99$w3GS8c!GFr*IxVe^OnTrvk8j!W-8?LEsc zVhVv+=Jywj&yxFNoT|G;uTSum;(n!{)I14DPJG?_!%o+D8&l`whKFqIfS&@I#J zOq}{{5CM~j6&Z-;nD`Qf=^I3t6g=Lq%Zfyz)oO8)V#fHUy*!o~Y;2|pOpE@Mw7&co zkQaM56+TKek@6-nf9wqGZ>cq8zn*~gSvIRp-v>8RGsn$j*pRpg{vdYDlBu5hR6rR= zEvB@z6#8`F$J2gMQdIP6h!du<1R_cjoxQy*bp^K#hT(-=8$(GhuC^?Gxek1J+p;Xo9t<5}%_Uh{$Oo*(povF>@T zuk2UvE=L!kkw<#t4}@z?l6|S!vkx z-{HClBu*1(6!)y?xj;s5%H~ASBA-$l7Dl7n;l+Hn3dku=PfwlJPt4C<-_kGhtRiZx z%_WT&Oj2$x{VHzBEqrUsXB67`?|~vB zL)dKRSK!vxsJ7d%Wd$zTNgEhKLnXD9h9Po-n3#A6J<;w_1{4^4V|#h7M zg#18SQr>U_u;&PyZuQU3rddsD1H>Sa-RRE>mj&iKx1#KB-&w9c`|*nNiN?!C*(^l@ zdif6sG%R16qoo1BbX##J)*d=fc668=1x7)4n1|FwW(aZ}5>G4S4CATdD@-;U^jaGBc6V1V7FJ=G{SnC$P9YbIf7|&rsq&DZmeD5A0RV;X{L9@Hd&{vA-UAoxIhI?hGU`!~mSmt)qLYb(xo8DH9 z;YtjPeE)%th=^zzhd}&?A-;3+sGKpW|D0<5_j@3m{P#WWE-u1E+&EVZKZSyTNHpZQYL_tTd~f-YJatpavH~*UThcg z&?$caLS_>cwyCZboR`l+TKm`?#V_8Ju<_@)|E-A=aN}>J8 z{YD*+ny+du+g`@-Qs^5SEB94Sja6HaScpOv!yxCIS-@%ulwTN%PQwYs z^LU4PaHy0QEnl_z1xG zJYe&?H$%_{J@{A_9jmpts5b&-nfFhV}dugcF)5o%O`ZoZ?sLz-L?H4R}O;XPF zmzto$Sv~A@17{=X#aP>0=Lw+6m=LG>=GTwE;2R(paC}{DR~!<{bnb!wDj}1s$GzPA zdZmIDc=_4T>+I%n{Q51qK|FrFSk$2t7(T$F_)u8MNbV2 zG}!DdZBMM7>72(w*W>823L)l=#;KY*9ATJ+wy{}@cQur~RBgKuQ9nySeJ^G_Ug)Wt zm`JvE#E!d7#4A+)Qw}mQV9DCu!C7|<=$;QQRJewUOi}wsIHOjqKLL5+0Ba(q)-gLO zVj&j#juq+6fvF4wt&H7ORnqNH2J2IhcG?+9HYh%xG49Kne@7JZ} z!zYKvp%hLZvtO8yZXmmJ=iKG;0-zi@HxAFg;N@nm>5AGWpF;;2zU{2c&#>LjZ{NOc zHXbtTi|K_=c8|jUJ4gRAcCz{-K0bcxDgHa`yFvRKLKEL5M9ckh61m?rB)P(OUu^CC z2r#0(ogHBq>fD*s?&<85l9TgVh-V1JH=g*ZS!KEn%QPB-=wbBcLHnxwd&lOg{@W5@o^a9bl4i?XjV1#vb zjS_(jdJ#G7doz&XAtLfmG8}d1gYH~jwxHiahm{f}J*UTnxMW!P&&7RwHJMekwUZkg zQ^qX$-TwvNwGb9dKDavyr+XzO=#(yoX|9eu{@&sff)fJHBU4i(nx7t79vYmgv;j<8 z@>+>df{l?8iTKWnOUp{rlrmSfmbW$-1D@V6bQWaY0Ap21?oy`<#L;j@>H45~*Bp22 zRa&Utz{R=i$I66(0Fme5>&N%+#}fVq@-YJgzq&kU%)DxhR<&JE|2v(7)2%s>$h-G1 zdU%UjSuy*uva%vfy^Y9xq->rS(RI4G9OEi-Cy|T)Fugr7>GtZDKPm_3flfo+fT?st zR9^5=*QeZ|pkZi4xPeUDr!H;$n^_5yN?1g!)(SKm@Ti4%Km-Llem5-15QoB8`}!S8 zaBv9583qWGT;>6V{RP7ERK=ELUxzG`E9 zsFtyi2x+`c8G8edPS;^)oVoC}U6}`>mdesaNEumke z(ded$h0k{lf0W(0j3EDZHA>!0Vx6H5OMXFMkXOix5}m}>x=nBG6|pfn6?ZVacR01G z(cr#TZXrdS+cq>rXn@O#l89gAbtgeObPLg75{7bxzI|rzQ|z`q*!OGV(C3xXu*QQ2 zuG(Ba#f^=P$@`=e`8a(g=uFk05s0lx?$lZtpX=?U2BO|{4_v8VD;beOGH|AuaVfo! z&>SqqH_1okxjvzMxMnvH?I%m!SO`(fBv}(lV2>bC=(M;X?Zp(ECS;_%Ej9?-R3=A}cloSsvKP#(OpZjOqZfryfFPrJiV!nVNHi9+rCwSj#9WK~m zzT3z3=0y6r>X(L=2vma5Y3y4K3{wXG>#Rwe_&tg_eMdOycN%}z3?pbZU z+o#;yH<3SBeoWUoO+(*#c6~h~=OJb!stDM%xx?n{WTotOARY#6a!f)36%;3dX*^ec zo!DzgG<3fNN{ZF{=xGBkU$6WdH+FSytqBMaiQOj?WI!OxtlE6m#z2+?}tLVppU@Eh*N3h znc2BHISP?txLzVUDMdxBxx*BcL~mgN9vZq=g+t`b5t|UxRRZ9Qyp=)Du&2+SB}!-h zDg!o+u8_CvC)RFSzsMfrmwi7F2pG6&b2S8~W%qHK0cPvP)8OgCDsYY`js-Gl<_V=T#Sy~?KrmJk9486ED`CQf?)!y2wpWo`fvAoRZxMu1TM74j1ocwOcitlz6XyRyT znnp)QJ31Esl&?msE*Ap>!#$4EB<)R@GcFIS=p+kSSh)`$WswiRC4R{u zm+I+z;&!mn_q-2C;d@(KRgUNUhN(2j$oDGDI@{aZ*KgjGSIRHjjbQr5R$QzjlN5?d z$G#7d96xrRoJexM%2)q>km7i@H*^5{UR5UJ)+QLHSWcBFefUZxR0G|5v3^!!iKux} zf3!5ZhryQYWRovsrFb!En7Ta=mhWTVqB=WSC1V?qx3!(GudDONJDmmB*CLDN(ea`d zHXh}L;|Ol@;$)EUBm#*mNz2hKft=wbE`ca^8b4=kZxsJ$S(%|w$$PVrlYhNYcA8QX ztoOgzFZRy=Gdy%%Oaca#R19BC((ZX3DM;{NCa{TuXZ@z$(A<= zrx5&WGsg0{p~P}|Copi!+Z&Oa1#W^EsF5zrhWc&|RCo?Aq&Tgc4`r5TWl0nUEHykB z32d`0NLJ-aRhK%XQt!M0CK6r-{y)0xFlj1?iS$POdJXqo+{&sT?J6~u?qT-p;-f#^?ZK=WBl zYOet33w-)~d@cxteDnlKPR_$%0`Y7~ef<|*>q2gaJA#QH+)aQ&KHv3eclXdAHC1hI z(jAkJ`u^+RUW(I3XlUTp;h~Ze5$)IKf8@q1v9SXjOs5Ns*H?f%_HFV@yL}&*h}*&1 z@FSIL@6Pl26ySb4qXYTMzp7~aTQ3YoTAyg7eo8I9@@UC1glv0FRf-MSL{Lpajqog- z+<+CQgs$=6Pj0?OxlOfUjFIr#VRKnwCLY<&`(|B~1T@1P3+qlw{wW9v!N+xZ`O$kR z@{X`lW8dlKW7R5CP;sO&ws&+KuAZVjeSUi$D(c=JKQtT80-#?XlBQZJ88%fvg@#(; zeydgG#Q&9DVu+d%+0@bcRD+3F+?%d5DP=SIW1@gTJI}P2$mqOXx#jRIyvb3TIb)Wd zyX&LHxUZEc`3<*znGIpzqR=l+NV;XBYP$4{dVhC&8>Hi-quLPa9LH-GJYeX+r2y6A9L6r2=XfZ6NPe2~TL^UvWtwR_y$2d)YV?!#SsYGSEpo{Bdd&(ThWnj?M9WnHZF~&%k=yW&Pe{?EWj`@hwCCC#};Tn_eq6mwA3+#V(?-NzA&AR)0VH;_uTa zs@xgo?h=#_^qwJp&Hw%TXOjM%B{?r+1p6!!93Ros-xs*g$jHji|3ZnYejz>6Y-5K7 z3nMlr#s%tZJh_NqTU#V;{X;>&$i=a;vc2f&@u?|om_a#FpU5n~Ad=8iBc}C7{ba@M zW(Ri#Xymc3VS#gGskxzsFlwrVne0COiRXyghIfxF1v`4{b7k?{vRw1mhNDGHmAcId1mD8 zs)g(fQiGr(v7UH-uyatLQYriD70P#Ji{%-ds&7&8>Q2A&1)Pg=PlBy2(QS$MC{ZNm z&D@+WTg_!DZ&VkUBmVLx*C6Cg3y{f1UKyG!*fgiDLmMax_)CyCv?t z5f2eplF!o_(uJU3`=?ot5HnWjaQ>jdD8ivz*5*IM#uMVSCTLehVfterFyvoPV0l(v zPx*@&@TQw|LY~FEx{3Z`&|&2g2R-7q&YHrq6$UPy)bKFT;XD?`&RXP} za8HS$gy~M$_nGDv$i!sLP_KADovmy}fvFZ?Rf)&{vlCexcWkXq zmlCg+xXXGYXgRD&U*o4!>J0IIZ{4uE;7$9ctvM#4D#eLG25P-8C(U0LTZ zowctkQgY7@BGTBJukNqv4w-Mi7ys&iO&d`-qCvO3Q+5A_5vQSty?3R2GFuS+92V*5 z+IbUN1PthDiO#QPygLy}@XR35q>)$%ajd&oa8TW-qU9fjze4Z&rJD918E#5G0`ZEW z6oaY!*FbgV)gN6A8=FL`2w49&b%bAV{wv!4&)@z3{2uLcf=2g$Ix(Di5%4(+<~5~g zQ=ML_9R2q(y;5)A?n=R-#S}N?#Cb(h%ta%WlWB&e4Q1=F{p`JG$C`i(Ni?}1#1*f3 zU;X15YCYK@m665~%Y&Db_dt7mF*i)v5Pa1u5yP zVtsl%yi&Rc3?Cz-qCEEhlz;aT_Z>jpA-qFKn16Lf^SY4p|IaHUeykC4flMW^C&v`6 zH<3NHh;^B%7{}M;#EHbndH!1B+ScaXz_YO!i7Np$F7lCR4AIGO#u;{om)H#SyS;c! zAdSAmlKTz0LR3@~#4By)4t(>CT|aSY4N3h`K5XrzccjncMAmpI` zP$~wZb{EfHfBUbsp|N&-+K>P&u$Mh~*1$+re)S5@y8!{u#LN8-UF$~+-e^dj+;Bi3 zz0(*!FxfJ7>4x6MJ;H5D@*D+)99dNXTQ z^_OdmQcb!_r*NOMY@5T$LY&4ANW1km+xi^`>(g$9u>rByP+uMz4lO(`HTI~5_UF!g zm&eX>2baZb3={&Bf5KG6^ZVEJai?Fv=5Di>Ns?XeoA_<$f0ImBgaaPPOfjvjax9s;ssHn-Jjl6DYiTdA zc7US7Ia7*IW{A{(zs%J?K*G_bIAnND{?F1y{{gkfA$ElD_m7LI>FuPY6dYzdUp=W0 zZs6idKMz$g+!t$}#pgI!O2>1Y65;^$%$eDGVV>j$|)>41IYDP`*JN*l~m8(Qe& zQZh=rf-mJExX3`yg@%p`DlLpO#@e@dev!I{XiUqj%HD1~Pw?&C!vwU_T|L_b5BbcH>g5Mq4fU+c74b@w(D4ctnx#x;0HCv7yt&uEdCJP}Yx^ zoSI54-eeL*=zh6;R1+QG%Ay;3O{|J1MnUMSj$Kz{-x*7=K48kKf2&ecQ_H_rjT!cm zNn>n08@y3ix8swEZekl0Pc5EZWdlAhkdHwpv76qG;!wZ=A*$tlwT$j;A_#xl*v#jA z0$iF`!|tvxHl8F3CgEDdn`5=7@%ARhKNE_LClkou=FkGL6;wEr)fOhk#+g@&CjT%v zF}AbfVzRP`SZ{~6QBhIltCuA-+BCHMUAi>$f@bqxXdcXCJ_~!O%SYo7JuI^tu2E>? zjeR-mvhnZRkV=8(H^o|6Y0EOE2NWF;__Z-vG&(d?DwW=TdPwoR*18ZrwYm99+VRVW z?MgXXl?(rZ6i4&4IP`x9f~z%|*U@zn;_GAusGxXP2)=x|Jc3CiCb@a#f@nn(Y% z6zT{NI&UT`xajF6P<~+NeKTHh69>syG3KRS{ur`UI=UB+g66P3zRFUb{1J$BvVoC- z(HI6cp7p7jhPrveaLb<}y_S*38h3E9B;?7kY$+-)n^M6^`gKxjcW=*f&mleLv3dzp zIwN^UaZZla?5>XlMvau{v8T?Gs+w_q)zU_8yA#Ri1)I_j&fd?JL^MXhjkf z&P?5}vA>)>+Py^j&~!reghyy+?NY7Dj=&UGb!E9lo6QI_e8G-lCJp2JThy5d6g;L# z^t8?Rm_L+Xb#B3;gXTm6@oi~i1YRy2^^DV=M_YQn1O3&CCR44yZ)jTCC#=1~-lOg> zkg4@MOSbx~;))7v$kavOGnT54~vmdTeEIGQocAk z{?Z5F_A`(3B<97mwvdqm4FSNn5a*U(E@JHL?0|0sw$*Z*fwWKEDy=aQAB@rxw_M(7 zfHD?9Hrc`CWMpvuDKp(pD=2(R$z#max6RZo9nTHM1E~*m$EU|6-%4_F5;B*kqW$_wJO-}*W56`X)5|kY z$jb@N(U)GohZmPgI$~U!0nhdJK~OI0nc=K939Vm(zc0FPvc)7*s=1Q3E(nf=jQ&K& z#l;!DjHR4#vRO|kf4lN4Uch?ebG1DUHUa_C6zC+7&dJ>2?!ELBM(8eWr9xNUt#)y| z7W!X}CYMzMbD%oF!lQJbUm@~F=%WH~GgS1!<%7g(o^c2^fY8&u<1f&XZx17<=Ci2+ z)(9HRjGHaFS0CbZ7d5|YeilI)_K3RDZLv3L+-YxZjDtmFvv#x5o>x<_uP5%fCzx8r z!6mgjJjHQsy3}jrr!~F&iT-aAILMV?ah}$WEi!+;!|wc}LCDQ%^ADFzQ`33CL2n$N z?|kI=c=BA;c;%2}C8=31|N5VLi|s-BWGbphUaDgiGUF2y6M)E~ z{@qv!Vz-jq)_X9N`5BsLZS2`%fA&G*rQ`ZUwa2&|;CmCnQa*GFMl0BdON4ama04Zk$0T*$)L3`6V}}3+ z1y{1B1ROR)GAwyt({HT$;BZe1S#-%A&Joy+CGId%bFAiUrNLqY*#(22`>)M(;y1}e zZ@Lu2R&G+TZvBksuo%r>-*;pQpSvvq3kJCLxmK025o?n!x56BM$mFI+TXGCL*I0U5)Etje16|)s9vy={Z3))V! zDnC|lYj4;8ly_#EF=@5AtDO?gQbzdZg{Wyce#^7pYZYssEoB(+|Kf9G_L+|4saa~? zMtqU@QCjO%87FMuYG-Gs*ngzGjCBh#{w`aC2vlR>B{q`2mY<*Rd5T284~>t{Gps`FVAvAat;Hr1%j9OH>fbBTWTW;VL2DB@&Uf^$l6TVAsRt{k76o2#!Aq~yjou>2be<)&Ipz1=<-K_E=V$mJDBGkFvp zATV-rR=$@o-7v0@PJ1OG4|}4aNjgg4D{E_O1OGB16EqqNB>zsGPo?z{4s~7q#^CSQlwIr`x_?dVRe8yBS^>S5f$D;BR}YlEP|l$L*#129_h(n{i-vfL zJ0udC4x1r6g$jt*GNtN0LepzkS{rMVzU}ROmmEEDCO}Cw+W5saG3)U#QPW|FIX$H~AhJNy!lF&CR6EI7oQy0o}oh9c9 z&O?d2d$`lmxk|AhyelhtTG(X+O6i6#Sqk-$VwE)EC|8>ve*}RzrkE&_2DzUYqj@7L zCkKHD7%DzrT(=7Fd`Ci%d*AicnUKuErTB*5R=_8bJndT7?ZGzVQiv~eg*>f7h3XQf zM>SuXShIgC3wkacWpu~rhr*m9RMKF1jejHj)ACnOzU~sBw*%PZH;h0+t&Ou2Ru)%B zs}&cUeX+jftT^yLCJt#ce@D6;YY>{R%+ZbzwL6|If9H!uCFsx(-imx;F;-p*M}Ix4 z&up@Aoh5ISwdBRdcOsXjI(i3WVNP(xcH~asg>hlm7y%@`1aj% zi%}_u-%ro^(nUH#B`-RtM|%?+X&8BzTJTPR*Lc=Bbtxx_)c{CvU!{d}YBD?f-t1J$47d?V4JUm>x#O68+G9gDC zc@qnx8!Cpb59TE}KvVSCeoq2AFVEwZ3`iuh{!_d&L5lYQ8pUp~5V}>g z<>YpFP~Uq}MCw%(VYBE7K029p*iGyBgH=l6zF754|?R|`xn|s8+M$mG0t?}9(cDg4SVU;WC znmXD~DuhA8S^&H{7bw8@3QK`g&~fG014bHCfEtC6F!8t`jq!ZPT67QC*QScCVNMEo zK3DO#zMmlYq#HtWY-SL%k;CT3VM~Cn1{DnqQ9?okBaO(?6b`JfL01{@UCF}s{Cvlm zW8E`<-w;!FHTz=YKZVs14I$Wf&riWBiv~oJgtPIx00q`$Ua(T)(DFAXi{ZMfrQsV9 z)i0%D2`^$xOUuwhY8FC8X-{ALY_o&OcD7@QUz9>d_hB^W{ay2hWR zuN0QorCqDWP2=91xCgn@*}d4*kXh@t$$@KsFrn7lN=HX`dpsDz7;ci8mM4T!ox%Eh zhufUj>N~HRLcx8l!^2&#^wm4IN?idRex?tFoh~RkY^|~$9KL>C@EY>^C`3FzXVcKM zj`IBIwq*C>SWT2qe+G*^8`IrAH`(LZyWKmWdtaZb*MP9;xZ=5kH9k}J>w#iS-Aw9r z4hwnKpozuWyOdVJy&zb=#9K$8&}nXHUenzbMBsm6zCuLoe06^Z!tO$ifzNg=Su2NN zS6V7gKgBy+TPnT>W?L)-bov}2+R=%9qYmnL;>XEDh7;);hd`^6iRYJ)kA^9YPQByX zKYM1V?^cCQgiN2Ixft`j2}vs-O-PXAxUzRvIo-2zXxr%I+F(!-HwtK5v)xQ!hL>AS zlA~T_*b%)Wfs;q#E3Q;?@v~(`lS~h;9*}$!T?g^GUw`>mi$z&22rJ=oLBeZTk~jW` z);Z83a^qz>V{leV2GqpZxD;Ch9RTAV=x7#eHt9fp*@aC6Av)Jg*eO+(DF2-#=70RW_)$kB86E->x zaQHc%(gr=U3cVKpN=0kAWcD%RN})B@kf@l2ackz+uK)t} z*`!BYL+QqIx}GeXBns7j)M&J7ujJr&T3VZ*|7I2kLyD$G&Hyx8 zQKj;YNo^kFcW|w=N2JWy)QtitnXi-t=*PkRk&%5TUh28ZZ?m49+&FChEi!wG7dG=F zg60uXoDgCJ@ijRY9s%@ZQRyQ8Z7)F@Qd*keu4mMq|Nj2nUSG3b>`8%!D+Q7UjZTos zy~zXvs9y%+f&T`{d8`C&&suE>+izal@%HWhhvXVc!B%3Q)vJmHbJN@OQZ=z&7^z|@ zUImOhz^0wz-NI^NjhlgUOawXKLte(`c8vMz6_!IaJ^nIxrj{=J2TcFDTx5{wBnWs} z0a4lS)km8!axO507n^J*yWU(0BNd)4?GveRRfY7bFv)ue(c#xwOVLKvdp30P1QPfN z(cAs!VH*dOkn!l~RVpB!4QCmlntf6>J3l%pZD zdbIw)H)xN*&%6!}R7^~1a*=OOPHzRjT!k7NvKoOrRG?LhUZ0Mt8kJA55u#_c-?y%#1)?!y#~qU~{zPL^*$Gca zIvlI!j!_7NprxH$H7Cy+o^c=qJI+&yapj)h!S{+llfIfh9x7uWejjl2S)rD_eO)ko z?HSS=MJlan{^Df7;F;o(0=*Q@_3Pi1N|ookY)ihHsY_Y8rAGmaV88VnA(X1ZQ^HUv zf(G>blSS8g^ zQ4zq1ujWX#^=-o3jZ2*}GJhj_zbIAnJQYXUL%V<#@+FYm$kwh%~C zc*{!xK#OwoUpP2BPSr0SqK&T|z`ukPIXxc(1O3(E{KaK6B8lp<`*u3I6J#KnRB}LJp1fFc; zumsn>Z+mjbEOB;wIzwPR^b|6Hgm&ey)|K;9F9J%+Wt%`bx{({=M~}waFcG&z4>}(p zT-P>_Cw0f3>T+TTwFdro_W2cNpAo+UF0SEtk7qnZVYVX>K-Km}M7`^#|KwQ9!uPCC z45EME)neTj59DFWdV7HP^{angNBl__n^YX!; zPqoQ=g5do|+{KW3?I7YNWEi)|*N*T;e=1+C^6Y0TIuZ;R;di*noo!Uw6eaF<(@Sl$ zwF7-cGPAWfW@5DRR3Ydd9jTg7w49?k_}+h3ZRV~@oIHL_B2QFambFn&HZJal2)r54)s#uGw$anS0c7Re&|hyN=~v(7?hqvi zb!bTqK1%SqFCD=|#;svYh?Dcj_P?`>%Ruz4fC=fslnnXCt4;MX2Ve5z*Cm2Frf${N zjj*?qHy2VD1)b5`f6IT3u6{})nslo(X$xD-!K+xWM?Sks!xzPmLuA7vrYPm>1MKXFPfYS0h~*{Z>6U4Cmxce`vP4M)RRwxhXPU_dtc>*R-A>z({a)K| z%{AXhH1IDHSc+O%j&I;=9mh(X3zJ<#u!-9HUo=DSD>#fKDin=UMl0oUQHe^u_$LYGjY?WDN{3lh&rJA zEs0S+;+}H9BxuX+o>!%w#S#M8cPqvm)&o_j1L#el`&-CT#WKKCk+K15h;l^E6wrxdT zDeBj;-UT3^f5|RF&{>-Sk@j

AJC?;d@v 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 z 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

`Bs=#lad9Q{(oTf4&m@iPnYRcJVdu{x| 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�CqL#GvgmUMRoYh*!R;`81UIXfg17K88cf17EB@ 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 A#sB~S diff --git a/scripts/kmp-bench/README.md b/scripts/kmp-bench/README.md index 83fca6daa52..f3fd142539b 100644 --- a/scripts/kmp-bench/README.md +++ b/scripts/kmp-bench/README.md @@ -50,15 +50,15 @@ Start-up here is from process start (the kernel's start time) to the marker, so | | Codename One | Compose Multiplatform | |---|---|---| -| Cold start | 1075 ms | 1349 ms | -| Warm start (median) | 623 ms | 840 ms | +| Cold start | 905 ms | 1097 ms | +| Warm start (median) | 572 ms | 638 ms | | Executable, stripped | 16.8 MB | 22.9 MB | -| App bundle, executable stripped | 18.2 MB | 22.9 MB | +| App bundle, executable stripped | 18.3 MB | 22.9 MB | | Bundle without executable | 1.3 MB | 2.5 KB | | Physical footprint | 49 MB | 48 MB | -| Recomposition, day change to drawn (median / p95) | 42.6 / 47.0 ms | 20.2 / 23.3 ms | -| Scroll, work per frame (median / p95) | 40.0 / 43.4 ms | 16.8 / 18.0 ms | -| Scroll, frame interval (median / p95) | 51.2 / 55.1 ms | 33.4 / 34.2 ms | +| Recomposition, day change to drawn (median / p95) | 4.2 / 5.3 ms | 19.5 / 24.0 ms | +| Scroll, work per frame (median / p95) | 5.1 / 6.3 ms | 17.1 / 18.5 ms | +| Scroll, frame interval (median / p95) | 34.8 / 38.8 ms | 33.4 / 35.3 ms | | Scroll, frames over 2x median | 0 of 144 | 0 of 144 | Runtime values are the median over 5 launches of each launch's median or p95. @@ -72,14 +72,22 @@ are linked in. The CN1 bundle holds 1.3 MB besides its executable: `iOSModernTheme.res` (213 KB), `default.metallib` (92 KB) and `kotlin.kotlin_builtins` (29 KB). -**CMP does each step in about half the time.** A day change takes about 42 ms -from state change to drawn on Codename One and 20 ms on CMP. One card of -scrolling takes 40 ms against 17 ms. Neither side dropped a frame against its own -median, but Codename One's frame interval (51 ms) is well past the simulator's -display interval, while CMP's is about two display frames (33 ms). These are -simulator numbers on an Apple-silicon Mac. They show where each runtime spends -CPU in identical code. They do not show device frame rates, GPU cost, or how a -finger-driven fling feels. +**Codename One does each step in about a quarter of the time.** A day change +takes 4.2 ms from state change to drawn on Codename One and 19.5 ms on CMP; one +card of scrolling takes 5.1 ms against 17.1 ms. Both sides then wait for the display: the frame interval is about two display frames on each +(35 ms and 33 ms), because `scrollToItem` is issued once per drawn frame, and +neither dropped a frame against its own median. These are simulator numbers +on an Apple-silicon Mac. They show where each runtime spends CPU in identical +code. They do not show device frame rates, GPU cost, or how a finger-driven +fling feels. + +The first version of compose-compat took 42 ms per day change, and almost none +of it was work: the EDT sat idle for 83% of it. A state change waited for a +dispatched resumption, then for the snapshot to apply, then for frame +callbacks sent after paint -- three EDT cycles. Frames now run at the start of +the layout pass, as Android's Choreographer runs them before traversal, and +the recomposer resumes inline on the EDT, so change, layout and paint share +one cycle. **Start-up is the simulator's floor.** An empty UIKit application, with no code of its own, takes as long to show its first frame as either side, so diff --git a/scripts/kmp-bench/chart.py b/scripts/kmp-bench/chart.py new file mode 100755 index 00000000000..2ee2a4f36a0 --- /dev/null +++ b/scripts/kmp-bench/chart.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Codename One designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Codename One through http://www.codenameone.com/ if you +# need additional information or have any questions. +# +"""Draws results.json as the guide's chart, every metric relative to CMP. + +Each row is one metric on the same axis: Compose Multiplatform is the dashed +100% line and the bar is Codename One's value as a share of it, so a bar half +as long means half the size or half the time. Both absolute values are +printed beside the bar. + + chart.py results.json out.svg +""" +import json +import sys + +FONT = 'font-family="Arial, sans-serif"' +CN1 = "#b5651d" + + +def rows(r): + """(label, cn1 value, cmp value, formatter) for every metric present.""" + mb = lambda b: "%.1f MB" % (b / 1048576.0) + ms = lambda v: "%.1f ms" % v if v < 10 else "%d ms" % round(v) + us = lambda k: (lambda x: x["perf_median_of_runs_us"].get(k, 0) / 1000.0) + key = lambda k: (lambda x: x.get(k)) + spec = [ + ("Executable, stripped", key("executable_stripped_bytes"), mb), + ("App bundle", key("bundle_bytes"), mb), + ("Physical footprint", key("footprint_bytes"), mb), + ("Cold start", key("cold_start_ms"), ms), + ("Warm start, median", key("warm_start_median_ms"), ms), + ("Recomposition, median", us("recompose-median-us"), ms), + ("Recomposition, p95", us("recompose-p95-us"), ms), + ("Scroll work per frame, median", us("scroll-work-median-us"), ms), + ("Scroll work per frame, p95", us("scroll-work-p95-us"), ms), + ("Scroll frame interval, median", us("scroll-frame-median-us"), ms), + ] + a, b = r["codename_one"], r["compose_multiplatform"] + return [(label, g(a), g(b), f) for label, g, f in spec if g(a) and g(b)] + + +def svg(r): + data = rows(r) + left, axis_w, row_h, top = 250, 420, 30, 70 + scale_max = max(1.25, max(c / m for _, c, m, _ in data) * 1.1) + px = lambda share: axis_w * share / scale_max + h = top + row_h * len(data) + 50 + w = left + axis_w + 200 + out = ['' % (w, h, w, h), + ' ' % (w, h), + ' ' + 'Keynote on an iPhone simulator: Codename One relative to Compose Multiplatform' % (w / 2, FONT), + ' ' + 'The dashed line is Compose Multiplatform (100%%); a bar is Codename One as a share of it. ' + 'Shorter is better on every row.' % (w / 2, FONT)] + for i, (label, c, m, f) in enumerate(data): + y = top + i * row_h + share = c / m + out.append(' %s' + % (y + 13, FONT, label)) + out.append(' ' % (left, y, px(share), CN1)) + out.append(' %d%% (%s vs %s)' + % (left + max(px(share), px(1)) + 8, y + 12, FONT, round(share * 100), f(c), f(m))) + x100 = left + px(1) + out.append(' ' % (x100, top - 8, x100, top + row_h * len(data) - 8)) + out.append(' ' + 'Compose Multiplatform = 100%%' % (x100, top + row_h * len(data) + 8, FONT)) + out.append('') + return "\n".join(out) + "\n" + + +if __name__ == "__main__": + with open(sys.argv[1]) as f: + results = json.load(f) + with open(sys.argv[2], "w") as f: + f.write(svg(results)) From aabeaef5e48dc69a6d87dd7cbbbb20ef9271c983 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 06:15:37 +0200 Subject: [PATCH 22/32] Stop safe-area forms laying out every EDT cycle; keep region repaints after a parent paint The padding a safe-area container sets for one measurement announced a style change, which queued a revalidate of the whole Form from inside its layout: a Form with a Toolbar on a notched device never stopped laying out (14-26% idle CPU on an iPhone simulator). It was left announcing because the JavaSE video peer went blank without it; the cause there was Component.repaint(x, y, w, h) dropping region repaints while repaintPending stayed set from a full repaint that a parent, not the component itself, had painted. Co-Authored-By: Claude Opus 5.5 --- .../src/com/codename1/ui/Component.java | 3 ++ .../src/com/codename1/ui/Container.java | 37 ++++++++--------- .../ui/SafeAreaRevalidateLoopTest.java | 40 ++++++++++++++----- 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/CodenameOne/src/com/codename1/ui/Component.java b/CodenameOne/src/com/codename1/ui/Component.java index 1e41c8273d6..3b00374f7d1 100644 --- a/CodenameOne/src/com/codename1/ui/Component.java +++ b/CodenameOne/src/com/codename1/ui/Component.java @@ -3100,6 +3100,9 @@ final void paintInternal(Graphics g, boolean paintIntersects) { if (!isVisible() || doNotPaint) { return; } + // Painted, whether as a root or by a parent: a later region repaint + // must not be dropped as already covered by a full one. + repaintPending = false; if (paintLockImage != null) { if (paintLockImage instanceof Image) { diff --git a/CodenameOne/src/com/codename1/ui/Container.java b/CodenameOne/src/com/codename1/ui/Container.java index 7d252c6f81c..7e9a9e561c6 100644 --- a/CodenameOne/src/com/codename1/ui/Container.java +++ b/CodenameOne/src/com/codename1/ui/Container.java @@ -2932,6 +2932,13 @@ private boolean snapToSafeAreaInternal() { } boolean changed = false; + // The padding set here lives for one measurement: the caller hands it + // straight back to TmpInsets.restore, which suppresses its events too. + // Announcing it made Component.styleChanged queue a revalidate of the + // whole Form from inside its own layout, so every pass queued the next + // and a form with a safe-area container never stopped laying out. + boolean suppressEvents = style.isSuppressChangeEvents(); + style.setSuppressChangeEvents(true); if (newPaddingTop != paddingTop || newPaddingBottom != paddingBottom) { if (!hasScrollableYParentInternal()) { changed = true; @@ -2964,31 +2971,19 @@ private boolean snapToSafeAreaInternal() { } } + style.setSuppressChangeEvents(suppressEvents); return changed; } - // NOT suppressed, deliberately -- see the history before changing this. - // - // The padding written by the safe-area snap is scaffolding: doLayout and - // calcPreferredSize set it, use it for one measurement, and hand it straight - // back to TmpInsets.restore, which has always suppressed events. Only the - // SETTING half announces, and a padding change is exactly what - // Component.styleChanged answers with revalidateLater() on the parent -- so - // laying out a safe-area container queues another revalidate of the whole - // Form, which lays it out again, which queues another. On a busy event - // dispatch thread that treadmill measured ~200ms per pass against ~15ms of - // real painting. - // - // Suppressing the announcement removes the treadmill and is WRONG here - // anyway: peer components rely on those repaints. The JavaSE video peer - // fills its buffer from the AWT side and never asks for a repaint itself - // (Peer.paint deliberately calls paintOnBuffer rather than cnt.repaint, to - // avoid a loop), so with nothing else repainting the form its frames never - // reach the screen -- a video that decoded correctly and displayed nothing. - // - // Fixing the treadmill therefore needs the peers to drive their own - // repaints first; until then the wasted passes are the cheaper defect. + // The safe-area snap's padding is suppressed (see snapToSafeAreaInternal). + // It was once left announcing because peers seemed to depend on the + // resulting treadmill: the JavaSE video peer showed nothing without it. The + // real cause was Component.repaint(int, int, int, int) dropping every + // region repaint while repaintPending was still set, and that flag was only + // cleared when the component was painted as a root -- never when a parent + // painted it. Component.paintInternal now clears it too, so a peer's own + // region repaints reach the screen without the form laying out each cycle. void doLayout() { doLayoutDepth++; diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/SafeAreaRevalidateLoopTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/SafeAreaRevalidateLoopTest.java index bf785e1f75b..c5915e4e48c 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/SafeAreaRevalidateLoopTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/SafeAreaRevalidateLoopTest.java @@ -33,12 +33,15 @@ import java.util.ArrayList; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * A safe-area container writes inset padding onto its own style, measures or lays out * with it, and hands the padding straight back. That round trip must be silent. * + *

The comment below is kept as the history of why.

+ * *

It used to announce the setting half as a PADDING style change, and * {@link Component#styleChanged} answers a PADDING change by queueing a * {@code revalidateLater()} on the parent. So laying out a Form with a safe-area @@ -80,7 +83,7 @@ private Container notchedSafeAreaForm(Form f) { } @FormTest - void measuringASafeAreaContainerAnnouncesItsPadding() { + void measuringASafeAreaContainerIsSilent() { Form f = Display.getInstance().getCurrent(); Container inner = notchedSafeAreaForm(f); @@ -88,18 +91,33 @@ void measuringASafeAreaContainerAnnouncesItsPadding() { inner.getStyle().addStyleListener(rec); inner.setShouldCalcPreferredSize(true); inner.getPreferredSize(); + inner.layoutContainer(); inner.getStyle().removeStyleListener(rec); - // It DOES announce, and that is load-bearing -- see the comment on the - // snapToSafeAreaInternal call in Container. Suppressing it removes the - // revalidate treadmill, and with it the repaints that peer components - // depend on: the JavaSE video peer fills its buffer from the AWT side - // and never requests a repaint itself, so with nothing else repainting - // the form its frames decode correctly and never reach the screen. - // Pinned so the optimisation is not reinstated without first making - // peers drive their own repaints. - assertTrue(rec.properties.size() > 0, - "measuring a safe-area container must still announce its padding"); + assertTrue(rec.properties.isEmpty(), + "the safe-area round trip announced " + rec.properties); + } + + /** + * What the announcement used to hide: a component painted by its parent + * kept the flag its own full repaint() set, so each later region repaint -- + * a peer's new video frame -- was dropped as already covered. + */ + @FormTest + void aRegionRepaintAfterAParentPaintIsKept() { + Form f = Display.getInstance().getCurrent(); + f.setLayout(new BorderLayout()); + Label child = new Label("peer"); + f.add(BorderLayout.CENTER, child); + f.show(); + f.layoutContainer(); + + child.repaint(); + Image img = Image.createImage(f.getWidth(), f.getHeight()); + f.paintComponent(img.getGraphics(), true); + + child.repaint(child.getAbsoluteX(), child.getAbsoluteY(), 4, 4); + assertNotNull(child.getDirtyRegion(), "a region repaint after the parent painted was dropped"); } } From 77d5c3ee25ae3144be48b59488f3f4319ab55a5e Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 06:15:37 +0200 Subject: [PATCH 23/32] kmp-bench: the same Keynote screens in Java as a third side; guide on dropping to Java and the platform API Co-Authored-By: Claude Opus 5.5 --- .../Kotlin-Multiplatform-Interop.asciidoc | 66 +- .../developer-guide/img/kmp-bench-results.svg | 96 ++- scripts/kmp-bench/README.md | 58 +- scripts/kmp-bench/app/java/JavaBench.java | 769 ++++++++++++++++++ scripts/kmp-bench/app/prepare.sh | 15 +- scripts/kmp-bench/chart.py | 58 +- scripts/kmp-bench/run_bench.sh | 33 +- 7 files changed, 997 insertions(+), 98 deletions(-) create mode 100644 scripts/kmp-bench/app/java/JavaBench.java diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc index c95c9ca1f49..00c7f86658f 100644 --- a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -20,19 +20,20 @@ reads. Only the UI layer -- `androidx.compose.ui`, `foundation` and `Graphics`. The same application, built from the same `commonMain` both ways and measured -on the same iPhone simulator, with Compose Multiplatform as 100%: +on the same iPhone simulator, with Compose Multiplatform as 100%. The second +bar is the same screens written in Java against Codename One directly, which +the next section explains: -image::img/kmp-bench-results.svg["Size, memory, start-up, recomposition and scrolling of the same Compose application built by Codename One, relative to Compose Multiplatform",scaledwidth=95%] +image::img/kmp-bench-results.svg["Size, memory, start-up, recomposition and scrolling of the same application built by Codename One from Compose and from Java, relative to Compose Multiplatform",scaledwidth=95%] -- **Recomposition and scrolling take about a quarter of the CPU time.** A - state change is drawn in 4.2 ms against 19.5 ms, and one step of scrolling - in 5.1 ms against 17.1 ms. Both sides keep pace with the display, so the - difference is headroom: for heavier screens, and for battery. +- **Recomposition and scrolling take a quarter to a third of the CPU time.** + A state change is drawn in 5.3 ms against 20.4 ms, and one step of + scrolling in 5.6 ms against 16.9 ms. Every side keeps pace with the display, + so the difference is headroom: for heavier screens, and for battery. - **The executable is 27% smaller** (16.8 MB against 22.9 MB) and the bundle 20% smaller, although the Codename One one carries the whole iOS port. -- **Start-up is faster**, 905 ms against 1097 ms cold and 572 ms against - 638 ms warm. -- **Memory is level**, 49 MB of physical footprint against 48 MB. +- **Start-up is faster**, 794 ms against 1051 ms cold. +- **Memory is level**, 48 MB of physical footprint against 49 MB. The benchmark, its method and how to run it are in `scripts/kmp-bench` in the Codename One repository. These are simulator numbers; they show where each @@ -46,12 +47,57 @@ Codename One form next to any other component, and reaches every Codename One API directly -- push notifications, in-app purchase, the native maps, the backend -- with no `expect`/`actual` plumbing per platform. -The same build also reaches targets Compose Multiplatform's UI does not run +The same build also reaches targets Compose Multiplatform's UI doesn't run on: watchOS, tvOS, and Windows, Linux and macOS as native binaries with no JVM to install. The Codename One backend runs the same Kotlin on the server, so a data class or validation rule in `commonMain` is shared by the app and the service that feeds it. +=== Dropping to Java where it pays + +A Compose screen and a Codename One screen are the same kind of object at run +time -- a component in a form -- so an application doesn't have to choose one +for all its screens. Kotlin calls every Codename One class directly, and a Java +class can sit beside the composables in the same module. Compose +Multiplatform has no such floor: below `commonMain` there is a different +language and a different UI toolkit on every platform. + +The benchmark measures what that floor is worth. `JavaBench.java` in +`scripts/kmp-bench/app/java` is the Keynote screens written against Codename +One directly, timed by the same steps, and it ships no Kotlin standard +library, no Compose runtime and no coroutines: + +- **The executable is 8.9 MB**, 39% of Compose Multiplatform's 22.9 MB and + about half of the Compose build's, and the bundle is 10.2 MB. +- **A step of scrolling takes 1.8 ms**, against 5.6 ms for the Compose build + and 16.9 ms for Compose Multiplatform. A day change takes 4.6 ms. +- **Memory is 53 MB**: it builds all fifty cards of a day as components, + where a lazy list composes only those in view. + +A hot list, a custom chart or a screen that has to fit a watch is where this +pays; the rest of the application can stay in Compose. + +=== One platform API on every target + +The `expect`/`actual` declarations a KMP project writes per platform are +mostly calls Codename One already makes the same everywhere. A composable +reaches each of them as ordinary Kotlin calls: + +- <>, in-app purchase and + subscriptions, and <>. +- <> and <> + hardening, as build options rather than per-platform SDKs. +- <> and + <>. +- Storage, SQLite, networking, the camera, location, sharing and the native + maps, with one implementation per platform maintained in Codename One. + +The targets go past phones and desktops too: Apple Watch and Wear OS from the +same project as the phone application, CarPlay and Android Auto, tvOS, +embedded Linux boards with no X11 and no JVM, the browser, and the +<<_server_side_backend,server-side backend>>. + === Quick start Create a Codename One application (for example at diff --git a/docs/developer-guide/img/kmp-bench-results.svg b/docs/developer-guide/img/kmp-bench-results.svg index 943dcd7d7d5..3644a5c60c5 100644 --- a/docs/developer-guide/img/kmp-bench-results.svg +++ b/docs/developer-guide/img/kmp-bench-results.svg @@ -1,37 +1,61 @@ - - - Keynote on an iPhone simulator: Codename One relative to Compose Multiplatform - The dashed line is Compose Multiplatform (100%); a bar is Codename One as a share of it. Shorter is better on every row. - Executable, stripped - - 73% (16.8 MB vs 22.9 MB) - App bundle - - 80% (18.3 MB vs 22.9 MB) - Physical footprint - - 102% (49.0 MB vs 48.0 MB) - Cold start - - 82% (905 ms vs 1097 ms) - Warm start, median - - 90% (572 ms vs 638 ms) - Recomposition, median - - 22% (4.2 ms vs 19 ms) - Recomposition, p95 - - 22% (5.3 ms vs 24 ms) - Scroll work per frame, median - - 30% (5.1 ms vs 17 ms) - Scroll work per frame, p95 - - 34% (6.3 ms vs 18 ms) - Scroll frame interval, median - - 104% (35 ms vs 33 ms) - - Compose Multiplatform = 100% + + + Keynote on an iPhone simulator, relative to Compose Multiplatform + The dashed line is Compose Multiplatform (100%); each bar is a share of it. Shorter is better on every row. + + Codename One, same Compose source + + Codename One, the same screens in Java + Executable, stripped + + 73% (16.8 MB vs 22.9 MB) + + 39% (8.9 MB vs 22.9 MB) + App bundle + + 80% (18.3 MB vs 22.9 MB) + + 44% (10.2 MB vs 22.9 MB) + Physical footprint + + 98% (48.0 MB vs 49.0 MB) + + 108% (53.0 MB vs 49.0 MB) + Cold start + + 76% (794 ms vs 1051 ms) + + 72% (754 ms vs 1051 ms) + Warm start, median + + 93% (556 ms vs 598 ms) + + 94% (560 ms vs 598 ms) + Recomposition, median + + 26% (5.3 ms vs 20 ms) + + 22% (4.6 ms vs 20 ms) + Recomposition, p95 + + 29% (6.8 ms vs 23 ms) + + 25% (5.7 ms vs 23 ms) + Scroll work per frame, median + + 33% (5.6 ms vs 17 ms) + + 11% (1.8 ms vs 17 ms) + Scroll work per frame, p95 + + 43% (8.1 ms vs 19 ms) + + 10% (1.9 ms vs 19 ms) + Scroll frame interval, median + + 119% (40 ms vs 33 ms) + + 114% (38 ms vs 33 ms) + + Compose Multiplatform = 100% diff --git a/scripts/kmp-bench/README.md b/scripts/kmp-bench/README.md index f3fd142539b..7c1f6aa73d4 100644 --- a/scripts/kmp-bench/README.md +++ b/scripts/kmp-bench/README.md @@ -48,18 +48,21 @@ iPhone 17 Pro simulator, Xcode 27, Apple silicon, Release builds, 5 runs. Start-up here is from process start (the kernel's start time) to the marker, so `simctl`'s own overhead is left out of both. -| | Codename One | Compose Multiplatform | -|---|---|---| -| Cold start | 905 ms | 1097 ms | -| Warm start (median) | 572 ms | 638 ms | -| Executable, stripped | 16.8 MB | 22.9 MB | -| App bundle, executable stripped | 18.3 MB | 22.9 MB | -| Bundle without executable | 1.3 MB | 2.5 KB | -| Physical footprint | 49 MB | 48 MB | -| Recomposition, day change to drawn (median / p95) | 4.2 / 5.3 ms | 19.5 / 24.0 ms | -| Scroll, work per frame (median / p95) | 5.1 / 6.3 ms | 17.1 / 18.5 ms | -| Scroll, frame interval (median / p95) | 34.8 / 38.8 ms | 33.4 / 35.3 ms | -| Scroll, frames over 2x median | 0 of 144 | 0 of 144 | +| | Codename One, Compose | Codename One, Java | Compose Multiplatform | +|---|---|---|---| +| Cold start | 794 ms | 754 ms | 1051 ms | +| Warm start (median) | 555 ms | 560 ms | 597 ms | +| Executable, stripped | 16.8 MB | 8.9 MB | 22.9 MB | +| App bundle, executable stripped | 18.3 MB | 10.2 MB | 22.9 MB | +| Bundle without executable | 1.3 MB | 1.3 MB | 2.5 KB | +| Physical footprint | 48.0 MB | 53.0 MB | 49.0 MB | +| Recomposition, day change to drawn (median) | 5.31 ms | 4.56 ms | 20.37 ms | +| Recomposition, day change to drawn (p95) | 6.79 ms | 5.67 ms | 23.08 ms | +| Scroll, work per frame (median) | 5.60 ms | 1.80 ms | 16.94 ms | +| Scroll, work per frame (p95) | 8.06 ms | 1.94 ms | 18.81 ms | +| Scroll, frame interval (median) | 39.52 ms | 37.91 ms | 33.34 ms | +| Scroll, frame interval (p95) | 45.00 ms | 43.98 ms | 35.05 ms | +| Scroll, frames over 2x median | 0 of 144 | 0 of 144 | 0 of 144 | Runtime values are the median over 5 launches of each launch's median or p95. An empty UIKit app measured 640-750 ms start-up and a 34 MB footprint on the @@ -72,14 +75,29 @@ are linked in. The CN1 bundle holds 1.3 MB besides its executable: `iOSModernTheme.res` (213 KB), `default.metallib` (92 KB) and `kotlin.kotlin_builtins` (29 KB). -**Codename One does each step in about a quarter of the time.** A day change -takes 4.2 ms from state change to drawn on Codename One and 19.5 ms on CMP; one -card of scrolling takes 5.1 ms against 17.1 ms. Both sides then wait for the display: the frame interval is about two display frames on each -(35 ms and 33 ms), because `scrollToItem` is issued once per drawn frame, and -neither dropped a frame against its own median. These are simulator numbers -on an Apple-silicon Mac. They show where each runtime spends CPU in identical -code. They do not show device frame rates, GPU cost, or how a finger-driven -fling feels. +**Codename One does each step in a quarter to a third of the time.** A day +change takes 5.3 ms from state change to drawn on Codename One and 20.4 ms on +CMP; one card of scrolling takes 5.6 ms against 16.9 ms. Every side then waits +for the display: `scrollToItem` is issued once per drawn frame, so the frame +interval is about two display frames on each, and none dropped a frame against +its own median. These are simulator numbers on an Apple-silicon Mac. They show +where each runtime spends CPU in identical code. They do not show device frame +rates, GPU cost, or how a finger-driven fling feels. + +**The Java column is the same app without Kotlin.** `app/java/JavaBench.java` +is the Keynote screens written against Codename One directly -- the same +programme generator, the same cards, the same steps timed the same way, a 1 px +marker painted after the list -- so it ships no Kotlin standard library, no +Compose runtime and no coroutines. Its executable is 8.9 MB, 39% of CMP's and +about half of the Compose build; a scroll step is 1.8 ms. Memory is the one +row it does not win: it builds all fifty cards of a day as components, where a +lazy list composes only those in view. + +Building it found a framework bug every Codename One app on a notched device +paid: a Form with a Toolbar laid itself out again on every EDT cycle, forever, +because the safe-area padding it sets for one measurement announced a style +change that queued another layout. The idle app burned 14-26% CPU and 10-20 MB +of garbage. `Container.snapToSafeAreaInternal` now sets that padding silently. The first version of compose-compat took 42 ms per day change, and almost none of it was work: the EDT sat idle for 83% of it. A state change waited for a diff --git a/scripts/kmp-bench/app/java/JavaBench.java b/scripts/kmp-bench/app/java/JavaBench.java new file mode 100644 index 00000000000..4784f82a9b8 --- /dev/null +++ b/scripts/kmp-bench/app/java/JavaBench.java @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.example.javabench; + +import com.codename1.system.Lifecycle; +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Font; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Graphics; +import com.codename1.ui.Label; +import com.codename1.ui.Tabs; +import com.codename1.ui.TextField; +import com.codename1.ui.Toolbar; +import com.codename1.ui.animations.Animation; +import com.codename1.ui.animations.Motion; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.plaf.Border; +import com.codename1.ui.plaf.RoundRectBorder; +import com.codename1.ui.plaf.Style; +import java.util.ArrayList; +import java.util.List; + +/** + * The Keynote application written directly against Codename One: the same + * screens, data and benchmark steps as the Compose source in app/shared, with + * no Kotlin, no Compose runtime and no coroutines in the binary. run_bench.sh + * builds it as the third side of the comparison. + */ +public class JavaBench extends Lifecycle { + static final int SESSIONS_PER_DAY = 50; + + final List sessions = Programme.generate(); + final List favorites = new ArrayList(); + int day = 1; + String query = ""; + + Form form; + Tabs tabs; + SessionList schedule; + SessionList favoriteList; + Insights insights; + Marker marker; + Container stepHost; + Perf perf; + + @Override + public void runApp() { + form = new Form("KotlinConf-ish 2026", new BorderLayout()); + form.getToolbar().setTitleCentered(true); + // Material's top app bar title: 22 sp, where the iOS theme sets a large title. + form.getToolbar().getTitleComponent().getAllStyles().setFont(font("native:MainRegular", 22)); + tabs = new Tabs(Component.BOTTOM); + tabs.setSwipeActivated(false); + tabs.addTab("Schedule", FontImage.MATERIAL_DATE_RANGE, 4, scheduleTab()); + favoriteList = new SessionList(this); + tabs.addTab("Favorites", FontImage.MATERIAL_FAVORITE, 4, favoriteList); + insights = new Insights(this); + tabs.addTab("Insights", FontImage.MATERIAL_INFO, 4, insights); + tabs.addSelectionListener((old, now) -> { + form.setTitle(now == 2 ? "Insights" : "KotlinConf-ish 2026"); + if (now == 1) { + favoriteList.show(favoriteSessions(), "Tap the heart on a session to keep it here."); + } else if (now == 2) { + insights.open(); + } + }); + form.add(BorderLayout.CENTER, tabs); + form.show(); + // A Form focuses its first focusable component; the search field's + // blinking caret would then repaint the transparent field, and with it + // the whole tab, every half second. Compose does not focus it either. + form.setFocused(null); + CN.callSerially(() -> System.out.println("KMPBENCH first-frame " + System.currentTimeMillis())); + if ("1".equals(System.getenv("KMPBENCH_PERF"))) { + perf = new Perf(this); + perf.start(); + } + } + + private Container scheduleTab() { + TextField search = new TextField("", "Search talks and speakers", 20, TextField.ANY); + search.setHint("Search talks and speakers", FontImage.createMaterial(FontImage.MATERIAL_SEARCH, search.getHintLabel().getUnselectedStyle())); + search.getAllStyles().setBorder(RoundRectBorder.create().cornerRadius(5f).strokeColor(0x79747e).strokeOpacity(255).stroke(1, false)); + search.getAllStyles().setBgTransparency(0); + search.getAllStyles().setPaddingUnit(Style.UNIT_TYPE_DIPS); + search.getAllStyles().setPadding(0.6f, 0.6f, 2.5f, 2.5f); + search.getAllStyles().setMarginUnit(Style.UNIT_TYPE_DIPS); + search.getAllStyles().setMargin(1.3f, 1.3f, 2.5f, 2.5f); + // Material's outlined field is 56 dp; the iOS theme's field is taller. + search.setPreferredH(dp(56)); + search.addDataChangedListener((type, index) -> { + query = search.getText().trim(); + refreshSchedule(); + }); + Container days = new Container(new FlowLayout()); + days.getAllStyles().setPaddingUnit(Style.UNIT_TYPE_DIPS); + days.getAllStyles().setPadding(0, 0, 2.5f, 2.5f); + for (int d = 1; d <= 3; d++) { + days.add(new DayChip(this, d)); + } + schedule = new SessionList(this); + marker = new Marker(); + Container top = BoxLayout.encloseY(search, days); + Container tab = new Container(new BorderLayout()) { + @Override + public void layoutContainer() { + if (perf != null) { + perf.layoutStep(); + } + super.layoutContainer(); + } + }; + stepHost = tab; + tab.add(BorderLayout.NORTH, top); + tab.add(BorderLayout.CENTER, schedule); + tab.add(BorderLayout.SOUTH, marker); + refreshSchedule(); + return tab; + } + + void setDay(int d) { + day = d; + refreshSchedule(); + schedule.getParent().getComponentAt(0).repaint(); + } + + void refreshSchedule() { + List visible = new ArrayList(); + String q = query.toLowerCase(); + for (Session s : sessions) { + if (s.day == day && (q.length() == 0 || s.title.toLowerCase().indexOf(q) >= 0 + || s.speaker.toLowerCase().indexOf(q) >= 0)) { + visible.add(s); + } + } + schedule.show(visible, "No session matches \"" + query + "\"."); + } + + List favoriteSessions() { + List out = new ArrayList(); + for (Session s : sessions) { + if (favorites.contains(Integer.valueOf(s.id))) { + out.add(s); + } + } + return out; + } + + void toggleFavorite(Session s) { + Integer id = Integer.valueOf(s.id); + if (!favorites.remove(id)) { + favorites.add(id); + } + } + + static int dp(float v) { + return CN.convertToPixels(v * 0.15875f); + } + + private static final java.util.Map FONTS = new java.util.HashMap(); + + /** One native font per face and size, shared by every card. */ + static Font font(String face, float sp) { + String key = face + sp; + Font f = FONTS.get(key); + if (f == null) { + f = Font.createTrueTypeFont(face, sp * 0.15875f); + FONTS.put(key, f); + } + return f; + } + + static boolean dark() { + Boolean d = CN.isDarkMode(); + return d != null && d.booleanValue(); + } + + // --- data --------------------------------------------------------------- + + static final String[] TRACK_LABELS = {"Language", "Multiplatform", "Server", "Tooling", "UI"}; + static final int[] TRACK_COLORS = {0x7F52FF, 0x0095D5, 0x2E7D32, 0xE65100, 0xC2185B}; + + static final class Session { + int id; + String title; + String speaker; + String company; + int track; + int day; + int start; + int length; + String room; + String description; + + String timeLabel() { + return clock(start) + " - " + clock(start + length); + } + } + + static String clock(int minutes) { + int h = minutes / 60; + int m = minutes % 60; + return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m; + } + + /** The same generator, seed and order as Data.kt, so both sides show the same 150 sessions. */ + static final class Programme { + static final String[] FIRST = {"Ada", "Linus", "Grace", "Alan", "Barbara", "Ken", "Margaret", "Dennis", "Frances", "Edsger", + "Radia", "Bjarne", "Sophie", "Guido", "Hedy", "James"}; + static final String[] LAST = {"Lovelace", "Hopper", "Turing", "Liskov", "Thompson", "Hamilton", "Ritchie", "Allen", + "Dijkstra", "Perlman", "Wilson", "Lamarr", "Gosling", "Kay", "Knuth", "Backus"}; + static final String[] COMPANIES = {"JetBrains", "Google", "Square", "Netflix", "Touchlab", "Cash App", "Shopify", "Zalando"}; + static final String[] TOPICS = {"Coroutines", "Flows", "Compose", "K2", "Ktor", "Gradle", "Serialization", "Wasm", + "Native interop", "Context parameters", "Value classes", "Multiplatform libraries", "Testing", "Performance", + "Accessibility", "Animations", "State hoisting", "Server-side Kotlin", "Build speed", "Code sharing"}; + static final String[] SHAPES = {"Deep dive into %s", "%s in production", "What's new in %s", "%s: lessons learned", + "Mastering %s", "%s from scratch", "The hidden cost of %s", "%s at scale"}; + static final String[] ROOMS = {"Auditorium", "Room 1", "Room 2", "Workshop A", "Lightning stage"}; + + private static long seed; + + private static int next(int n) { + seed = seed * 6364136223846793005L + 1442695040888963407L; + return (int) ((seed >>> 33) % n); + } + + static List generate() { + seed = 20261025L; + List out = new ArrayList(); + int id = 0; + for (int day = 1; day <= 3; day++) { + int start = 9 * 60; + int count = 0; + while (count < SESSIONS_PER_DAY) { + for (String room : ROOMS) { + if (count >= SESSIONS_PER_DAY) { + break; + } + String topic = TOPICS[next(TOPICS.length)]; + String title = replace(SHAPES[next(SHAPES.length)], topic); + String speaker = FIRST[next(FIRST.length)] + " " + LAST[next(LAST.length)]; + String company = COMPANIES[next(COMPANIES.length)]; + Session s = new Session(); + s.id = id++; + s.title = title; + s.speaker = speaker; + s.company = company; + s.track = next(TRACK_LABELS.length); + s.day = day; + s.start = start; + s.length = "Lightning stage".equals(room) ? 15 : room.startsWith("Workshop") ? 90 : 45; + s.room = room; + s.description = title + ". " + speaker + " of " + company + " walks through " + topic + + ": the model behind it, the mistakes teams make with it, and the patterns that hold up in real applications. " + + "Bring questions; the last ten minutes are open Q&A."; + out.add(s); + count++; + } + start += 60; + } + } + return out; + } + + private static String replace(String shape, String topic) { + int i = shape.indexOf("%s"); + return shape.substring(0, i) + topic + shape.substring(i + 2); + } + } + + // --- components --------------------------------------------------------- + + /** A Material filter chip: outlined, filled when selected. */ + static final class DayChip extends Button { + private final JavaBench app; + private final int day; + + DayChip(JavaBench app, int day) { + super("Day " + day); + this.app = app; + this.day = day; + Style s = getAllStyles(); + s.setFont(font("native:MainRegular", 14)); + s.setPaddingUnit(Style.UNIT_TYPE_DIPS); + s.setPadding(1, 1, 2.5f, 2.5f); + s.setMarginUnit(Style.UNIT_TYPE_DIPS); + s.setMargin(0, 0, 0, 1.3f); + s.setBorder(Border.createEmpty()); + s.setBgTransparency(0); + s.setFgColor(dark() ? 0xE6E1E5 : 0x1D1B20); + addActionListener(e -> app.setDay(day)); + } + + @Override + public void paintBackground(Graphics g) { + boolean on = app.day == day; + int r = dp(8); + g.setAntiAliased(true); + if (on) { + g.setColor(dark() ? 0x3A2D6B : 0xE9E1FF); + g.fillRoundRect(getX(), getY(), getWidth(), getHeight(), r * 2, r * 2); + } else { + g.setColor(0x79747E); + g.drawRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, r * 2, r * 2); + } + } + } + + /** + * The scrolling list of cards. Cards are kept and rebound when the day or + * the query changes, as a lazy list reuses its item nodes. + */ + static final class SessionList extends Container { + private final JavaBench app; + private final List cards = new ArrayList(); + private final Label empty = new Label(); + private int shown; + + SessionList(JavaBench app) { + super(BoxLayout.y()); + this.app = app; + setScrollableY(true); + getAllStyles().setPaddingUnit(Style.UNIT_TYPE_DIPS); + getAllStyles().setPadding(1.3f, 1.3f, 2.5f, 2.5f); + empty.getAllStyles().setAlignment(Component.CENTER); + empty.getAllStyles().setFgColor(0x49454F); + } + + void show(List list, String emptyText) { + while (cards.size() < list.size()) { + Card c = new Card(app); + cards.add(c); + add(c); + } + for (int i = 0; i < cards.size(); i++) { + Card c = cards.get(i); + boolean visible = i < list.size(); + if (visible) { + c.bind(list.get(i)); + } + c.setHidden(!visible); + c.setVisible(visible); + } + if (list.isEmpty()) { + empty.setText(emptyText); + if (empty.getParent() == null) { + add(empty); + } + } else if (empty.getParent() != null) { + removeComponent(empty); + } + if (shown != list.size()) { + shown = list.size(); + revalidate(); + } + repaint(); + } + + /** The list's scrollToItem: card i at the top. */ + void scrollToCard(int i) { + int y = cards.get(i).getY() - getStyle().getPaddingTop(); + int max = Math.max(0, getScrollDimension().getHeight() - getHeight()); + setScrollY(Math.max(0, Math.min(y, max))); + } + } + + /** One session: track stripe, time and room, title, speaker, favorite heart. */ + static final class Card extends Container { + private final JavaBench app; + private final Label time = new Label(); + private final Label title = new Label(); + private final Label speaker = new Label(); + private final Button heart = new Button(); + private Session session; + private boolean shownFavorite; + + Card(JavaBench app) { + super(new BorderLayout()); + this.app = app; + Style s = getAllStyles(); + s.setBgTransparency(0); + s.setBorder(Border.createEmpty()); + s.setMarginUnit(Style.UNIT_TYPE_DIPS); + s.setMargin(0, 1.9f, 0, 0); + s.setPaddingUnit(Style.UNIT_TYPE_DIPS); + s.setPadding(1.9f, 1.9f, 2.9f, 0.6f); + style(time, "native:MainRegular", 12, 0x49454F); + style(title, "native:MainRegular", 16, 0x1D1B20); + style(speaker, "native:MainRegular", 12, 0x49454F); + title.getAllStyles().setMarginUnit(Style.UNIT_TYPE_DIPS); + title.getAllStyles().setMargin(0.6f, 0.3f, 0, 0); + heart.getAllStyles().setBgTransparency(0); + heart.getAllStyles().setBorder(Border.createEmpty()); + heart.addActionListener(e -> { + app.toggleFavorite(session); + updateHeart(); + }); + add(BorderLayout.CENTER, BoxLayout.encloseY(time, title, speaker)); + add(BorderLayout.EAST, BorderLayout.centerAbsolute(heart)); + addPointerReleasedListener(e -> { + if (session != null && !heart.contains(e.getX(), e.getY())) { + new Detail(app, session).show(); + } + }); + } + + private static void style(Label l, String face, float sp, int color) { + Style s = l.getAllStyles(); + s.setFont(font(face, sp)); + s.setFgColor(color); + s.setPadding(0, 0, 0, 0); + s.setMargin(0, 0, 0, 0); + s.setBgTransparency(0); + l.setEndsWith3Points(true); + } + + void bind(Session s) { + session = s; + time.setText(s.timeLabel() + " - " + s.room); + title.setText(s.title); + speaker.setText(s.speaker + ", " + s.company); + updateHeart(); + } + + private void updateHeart() { + boolean fav = app.favorites.contains(Integer.valueOf(session.id)); + Style s = heart.getAllStyles(); + s.setFgColor(fav ? 0xE91E63 : 0x49454F); + if (fav != shownFavorite || heart.getIcon() == null) { + shownFavorite = fav; + FontImage.setMaterialIcon(heart, fav ? FontImage.MATERIAL_FAVORITE : FontImage.MATERIAL_FAVORITE_BORDER, 3.8f); + } + } + + @Override + public void paintBackground(Graphics g) { + // The rounded card in the track colour, then the card colour over + // all of it but a 6 dp stripe, whose inner edge stays straight. + int x = getX(); + int y = getY(); + int w = getWidth(); + int h = getHeight(); + int r = dp(12); + int stripe = dp(6); + g.setAntiAliased(true); + g.setColor(TRACK_COLORS[session == null ? 0 : session.track]); + g.fillRoundRect(x, y, w, h, r * 2, r * 2); + g.setColor(dark() ? 0x1D1B20 : 0xF7F2FA); + g.fillRect(x + stripe, y, w - stripe - r, h); + g.fillRoundRect(x + w - r * 2, y, r * 2, h, r * 2, r * 2); + } + } + + /** Drawn after the list in every frame; the benchmark times a step until it paints. */ + static final class Marker extends Component { + int seq; + int drawnSeq; + long drawnAt; + + @Override + public void paint(Graphics g) { + drawnAt = System.nanoTime(); + drawnSeq = seq; + } + + @Override + protected Dimension calcPreferredSize() { + return new Dimension(1, 1); + } + } + + /** Sessions per track for the selected day, as bars that grow in. */ + static final class Insights extends Container { + private final JavaBench app; + private final Label caption = new Label(); + private final Container legend = new Container(BoxLayout.y()); + private final Chart chart = new Chart(); + + Insights(JavaBench app) { + super(BoxLayout.y()); + this.app = app; + getAllStyles().setPaddingUnit(Style.UNIT_TYPE_DIPS); + getAllStyles().setPadding(3.2f, 3.2f, 3.2f, 3.2f); + caption.getAllStyles().setFont(font("native:MainBold", 16)); + add(caption); + add(chart); + add(legend); + } + + void open() { + int[] counts = new int[TRACK_LABELS.length]; + for (Session s : app.sessions) { + if (s.day == app.day) { + counts[s.track]++; + } + } + caption.setText("Sessions per track, day " + app.day); + legend.removeAll(); + for (int t = 0; t < counts.length; t++) { + Label name = new Label(TRACK_LABELS[t]); + Label n = new Label(String.valueOf(counts[t])); + n.getAllStyles().setFont(font("native:MainBold", 14)); + legend.add(BorderLayout.centerEastWest(name, n, null)); + } + if (!app.favorites.isEmpty()) { + Label fav = new Label("You have " + app.favorites.size() + " favorite sessions."); + fav.getAllStyles().setFgColor(0x7F52FF); + legend.add(fav); + } + chart.start(counts); + revalidate(); + } + } + + static final class Chart extends Component implements Animation { + private int[] counts = new int[0]; + private Motion grow; + + void start(int[] c) { + counts = c; + grow = Motion.createEaseInOutMotion(0, 1000, 700); + grow.start(); + getComponentForm().registerAnimated(this); + } + + @Override + public boolean animate() { + if (grow != null && grow.isFinished()) { + getComponentForm().deregisterAnimated(this); + } + return true; + } + + @Override + public void paint(Graphics g) { + int max = 1; + for (int n : counts) { + max = Math.max(max, n); + } + int gap = dp(16); + int r = dp(8) * 2; + int w = counts.length == 0 ? 0 : (getWidth() - gap * (counts.length - 1)) / counts.length; + float f = grow == null ? 1 : grow.getValue() / 1000f; + g.setAntiAliased(true); + for (int i = 0; i < counts.length; i++) { + int x = getX() + i * (w + gap); + g.setColor(0xE6E0E9); + g.fillRoundRect(x, getY(), w, getHeight(), r, r); + int h = (int) (getHeight() * counts[i] / (float) max * f); + g.setColor(TRACK_COLORS[i]); + g.fillRoundRect(x, getY() + getHeight() - h, w, h, r, r); + } + } + + @Override + protected Dimension calcPreferredSize() { + return new Dimension(CN.getDisplayWidth(), dp(220)); + } + } + + /** A session's own screen. */ + static final class Detail extends Form { + Detail(JavaBench app, Session s) { + super(TRACK_LABELS[s.track], BoxLayout.y()); + Form back = app.form; + getToolbar().setTitleCentered(true); + getToolbar().setBackCommand("", Toolbar.BackCommandPolicy.AS_ARROW, e -> back.showBack()); + getContentPane().getAllStyles().setPaddingUnit(Style.UNIT_TYPE_DIPS); + getContentPane().getAllStyles().setPadding(3.2f, 3.2f, 3.2f, 3.2f); + Label title = new Label(s.title); + title.getAllStyles().setFont(font("native:MainRegular", 24)); + add(title); + add(new Label(s.speaker + ", " + s.company)); + add(new Label("Day " + s.day + ", " + s.timeLabel(), FontImage.createMaterial(FontImage.MATERIAL_SCHEDULE, "Label", 3.5f))); + add(new Label(s.room, FontImage.createMaterial(FontImage.MATERIAL_PLACE, "Label", 3.5f))); + com.codename1.components.SpanLabel description = new com.codename1.components.SpanLabel(s.description); + add(description); + } + } + + /** + * The steps of Perf.kt, timed the same way: from the state change to the + * marker's paint, one frame between steps. + */ + static final class Perf implements Animation { + private final JavaBench app; + private final List recompose = new ArrayList(); + private final List work = new ArrayList(); + private final List frames = new ArrayList(); + private int phase; + private int step; + private int gap; + private boolean waiting; + private boolean requested; + private long t0; + private long last; + + Perf(JavaBench app) { + this.app = app; + } + + void start() { + System.out.println("KMPBENCH perf-start 0"); + gap = 10; + app.form.registerAnimated(this); + } + + @Override + public boolean animate() { + Marker m = app.marker; + if (waiting) { + if (m.drawnSeq < m.seq) { + return false; + } + waiting = false; + record(m.drawnAt); + // Perf.kt notices the paint in the next frame and steps in the + // one after; returning here keeps the same two frames per step. + return false; + } + if (gap > 0) { + gap--; + return false; + } + if (!requested) { + // The step runs at the start of the next cycle's layout pass, + // where a Compose frame runs: change, layout and paint then + // share one cycle on both sides. + requested = true; + app.stepHost.revalidateLater(); + } + return false; + } + + /** Called from the schedule tab's layout. */ + void layoutStep() { + if (requested && !waiting) { + requested = false; + next(); + } + } + + private void record(long drawnAt) { + long us = (drawnAt - t0) / 1000; + if (phase == 0) { + if (step > 30) { + recompose.add(Long.valueOf(us)); + } + } else if (phase >= 2) { + work.add(Long.valueOf(us)); + if (last != 0) { + frames.add(Long.valueOf((drawnAt - last) / 1000)); + } + last = drawnAt; + } + } + + private void mark() { + Marker m = app.marker; + m.seq++; + m.repaint(); + waiting = true; + } + + private void next() { + if (phase == 0) { + if (step == 330) { + report("recompose-median-us", pct(recompose, 50)); + report("recompose-p95-us", pct(recompose, 95)); + phase = 1; + step = 0; + app.setDay(1); + gap = 2; + return; + } + step++; + t0 = System.nanoTime(); + app.setDay(app.day % 3 + 1); + mark(); + return; + } + // phase 1 scrolls day 1 as a warm-up; phases 2..4 scroll days 1..3. + if (step == 0) { + app.schedule.scrollToCard(0); + app.schedule.repaint(); + last = 0; + step = 1; + gap = 1; + return; + } + if (step < SESSIONS_PER_DAY) { + t0 = System.nanoTime(); + app.schedule.scrollToCard(step); + app.schedule.repaint(); + step++; + mark(); + return; + } + if (phase == 4) { + app.form.deregisterAnimated(this); + long fm = pct(frames, 50); + report("scroll-frames", frames.size()); + report("scroll-frame-median-us", fm); + report("scroll-frame-p95-us", pct(frames, 95)); + int jank = 0; + for (Long f : frames) { + if (f.longValue() > 2 * fm) { + jank++; + } + } + report("scroll-jank-frames", jank); + report("scroll-work-median-us", pct(work, 50)); + report("scroll-work-p95-us", pct(work, 95)); + report("perf-done", 0); + return; + } + if (phase == 1) { + work.clear(); + frames.clear(); + } + phase++; + step = 0; + app.setDay(phase - 1); + gap = 2; + } + + @Override + public void paint(Graphics g) { + } + + private static long pct(List v, int p) { + Long[] s = v.toArray(new Long[v.size()]); + java.util.Arrays.sort(s); + return s[Math.min(s.length - 1, (s.length * p) / 100)].longValue(); + } + + private static void report(String name, long v) { + System.out.println("KMPBENCH " + name + " " + v); + } + } +} diff --git a/scripts/kmp-bench/app/prepare.sh b/scripts/kmp-bench/app/prepare.sh index 346bacfd1a3..0b366585132 100755 --- a/scripts/kmp-bench/app/prepare.sh +++ b/scripts/kmp-bench/app/prepare.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Materialises BOTH sides of the Kotlin Multiplatform benchmark into a work +# Materialises all three sides of the Kotlin Multiplatform benchmark into a work # directory, from ONE copy of the application. # # scripts/kmp-bench/app/prepare.sh --work /tmp/kbench [--maven-repo DIR] @@ -35,7 +35,7 @@ fi MVN_REPO_ARG="" [ -n "$MAVEN_REPO_LOCAL" ] && MVN_REPO_ARG="-Dmaven.repo.local=$MAVEN_REPO_LOCAL" -rm -rf "$WORK/cn1" "$WORK/cmp" +rm -rf "$WORK/cn1" "$WORK/cmp" "$WORK/java" mkdir -p "$WORK" # --- Codename One ----------------------------------------------------------- @@ -51,6 +51,17 @@ cp "$HERE/cn1/BenchEntry.kt" "$COMMON/src/main/kotlin/com/example/kmpbench/" cp -R "$HERE/shared/commonMain/kotlin" "$COMMON/src/main/kmp/commonMain/kotlin" echo "codename1.arg.ios.newStorageLocation=true" >> "$COMMON/codenameone_settings.properties" +# --- Codename One, written in Java ------------------------------------------ +# The same screens over Codename One directly: no Kotlin sources, so neither +# the Kotlin standard library nor the Compose runtime is in the application. + +( cd "$WORK" && mvn -B -q $MVN_REPO_ARG archetype:generate \ + -DarchetypeArtifactId=cn1app-archetype -DarchetypeGroupId=com.codenameone \ + -DarchetypeVersion="$CN1_VERSION" -DgroupId=com.example.javabench -DartifactId=java \ + -Dversion=1.0-SNAPSHOT -DmainName=JavaBench -DinteractiveMode=false ) +cp "$HERE/java/JavaBench.java" "$WORK/java/common/src/main/java/com/example/javabench/JavaBench.java" +echo "codename1.arg.ios.newStorageLocation=true" >> "$WORK/java/common/codenameone_settings.properties" + # --- Compose Multiplatform -------------------------------------------------- cp -R "$HERE/cmp/." "$WORK/cmp/" diff --git a/scripts/kmp-bench/chart.py b/scripts/kmp-bench/chart.py index 2ee2a4f36a0..24dcef69059 100755 --- a/scripts/kmp-bench/chart.py +++ b/scripts/kmp-bench/chart.py @@ -24,10 +24,11 @@ # """Draws results.json as the guide's chart, every metric relative to CMP. -Each row is one metric on the same axis: Compose Multiplatform is the dashed -100% line and the bar is Codename One's value as a share of it, so a bar half -as long means half the size or half the time. Both absolute values are -printed beside the bar. +Each metric is one group on the same axis: Compose Multiplatform is the dashed +100% line, and the two bars are Codename One running the same Compose source +and Codename One running the same screens written in Java, each as a share of +it. A bar half as long means half the size or half the time. The absolute +values are printed beside each bar. chart.py results.json out.svg """ @@ -35,7 +36,8 @@ import sys FONT = 'font-family="Arial, sans-serif"' -CN1 = "#b5651d" +SERIES = [("codename_one", "Codename One, same Compose source", "#b5651d"), + ("codename_one_java", "Codename One, the same screens in Java", "#2e7d5b")] def rows(r): @@ -56,37 +58,49 @@ def rows(r): ("Scroll work per frame, p95", us("scroll-work-p95-us"), ms), ("Scroll frame interval, median", us("scroll-frame-median-us"), ms), ] - a, b = r["codename_one"], r["compose_multiplatform"] - return [(label, g(a), g(b), f) for label, g, f in spec if g(a) and g(b)] + base = r["compose_multiplatform"] + out = [] + for label, g, f in spec: + vals = [g(r[k]) if k in r else None for k, _, _ in SERIES] + if g(base) and all(vals): + out.append((label, vals, g(base), f)) + return out def svg(r): data = rows(r) - left, axis_w, row_h, top = 250, 420, 30, 70 - scale_max = max(1.25, max(c / m for _, c, m, _ in data) * 1.1) + left, axis_w, bar_h, group_h, top = 250, 400, 13, 36, 92 + scale_max = max(1.25, max(c / m for _, vals, m, _ in data for c in vals) * 1.1) px = lambda share: axis_w * share / scale_max - h = top + row_h * len(data) + 50 - w = left + axis_w + 200 + h = top + group_h * len(data) + 40 + w = left + axis_w + 220 out = ['' % (w, h, w, h), ' ' % (w, h), ' ' - 'Keynote on an iPhone simulator: Codename One relative to Compose Multiplatform' % (w / 2, FONT), + 'Keynote on an iPhone simulator, relative to Compose Multiplatform' % (w / 2, FONT), ' ' - 'The dashed line is Compose Multiplatform (100%%); a bar is Codename One as a share of it. ' + 'The dashed line is Compose Multiplatform (100%%); each bar is a share of it. ' 'Shorter is better on every row.' % (w / 2, FONT)] - for i, (label, c, m, f) in enumerate(data): - y = top + i * row_h - share = c / m + lx = 30 + for _, name, color in SERIES: + out.append(' ' % (lx, color)) + out.append(' %s' % (lx + 18, FONT, name)) + lx += 18 + 7 * len(name) + 30 + for i, (label, vals, m, f) in enumerate(data): + y = top + i * group_h out.append(' %s' - % (y + 13, FONT, label)) - out.append(' ' % (left, y, px(share), CN1)) - out.append(' %d%% (%s vs %s)' - % (left + max(px(share), px(1)) + 8, y + 12, FONT, round(share * 100), f(c), f(m))) + % (y + 17, FONT, label)) + for j, ((_, _, color), c) in enumerate(zip(SERIES, vals)): + by = y + j * (bar_h + 2) + share = c / m + out.append(' ' % (left, by, px(share), bar_h, color)) + out.append(' %d%% (%s vs %s)' + % (left + max(px(share), px(1)) + 8, by + 10, FONT, round(share * 100), f(c), f(m))) x100 = left + px(1) out.append(' ' % (x100, top - 8, x100, top + row_h * len(data) - 8)) + 'stroke-dasharray="4 3"/>' % (x100, top - 6, x100, top + group_h * len(data) - 6)) out.append(' ' - 'Compose Multiplatform = 100%%' % (x100, top + row_h * len(data) + 8, FONT)) + 'Compose Multiplatform = 100%%' % (x100, top + group_h * len(data) + 10, FONT)) out.append('') return "\n".join(out) + "\n" diff --git a/scripts/kmp-bench/run_bench.sh b/scripts/kmp-bench/run_bench.sh index 3167b1d0997..dbd062df04d 100755 --- a/scripts/kmp-bench/run_bench.sh +++ b/scripts/kmp-bench/run_bench.sh @@ -24,8 +24,9 @@ # # Builds the Keynote app twice from the same Compose source -- once with # Codename One (compose-compat, ParparVM) and once with Compose Multiplatform -# (Kotlin/Native + Skia) -- runs both on one iOS simulator and writes -# results.json and results.md into the work directory. +# (Kotlin/Native + Skia) -- and a third time from app/java, the same screens +# written in Java over Codename One directly. Runs all three on one iOS +# simulator and writes results.json and results.md into the work directory. # # run_bench.sh --work [--runs N] [--device ] [--maven-repo ] # @@ -69,6 +70,17 @@ CN1_APP="$WORK/dd/Build/Products/Release-iphonesimulator/Bench.app" CN1_EXE="$CN1_APP/Bench" CN1_ID=com.example.kmpbench +echo "== Codename One, Java: translate and build" +( cd "$WORK/java" && JAVA_HOME="$JAVA17_HOME" mvn -B -q $MVN_REPO_ARG package -DskipTests \ + -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-source -Dopen=false ) > "$WORK/java-build.log" 2>&1 +JSRC="$(dirname "$(find "$WORK/java/common/target" -maxdepth 2 -name JavaBench.xcodeproj | head -1)")" +( cd "$JSRC" && xcodebuild -project JavaBench.xcodeproj -scheme JavaBench -configuration Release \ + -sdk iphonesimulator -arch arm64 -derivedDataPath "$WORK/dd-java" CODE_SIGNING_ALLOWED=NO build ) \ + > "$WORK/java-xcode.log" 2>&1 +JAVA_APP="$WORK/dd-java/Build/Products/Release-iphonesimulator/JavaBench.app" +JAVA_EXE="$JAVA_APP/JavaBench" +JAVA_ID=com.example.javabench + echo "== Compose Multiplatform: build" ( cd "$WORK/cmp" && JAVA_HOME="$JAVA17_HOME" gradle --no-daemon -q \ :composeApp:linkReleaseExecutableIosSimulatorArm64 ) > "$WORK/cmp-build.log" 2>&1 @@ -136,8 +148,9 @@ footprint_bytes() { v = $(i + 1); u = $(i + 2); print int(v * (u == "GB" ? 1073741824 : u == "MB" ? 1048576 : 1024)); exit } }' } -for side in cn1 cmp; do - if [ $side = cn1 ]; then app="$CN1_APP"; id=$CN1_ID; exe=Bench.app/Bench +for side in cn1 java cmp; do + if [ $side = cn1 ]; then app="$CN1_APP"; id=$CN1_ID; exe=/Bench.app/Bench + elif [ $side = java ]; then app="$JAVA_APP"; id=$JAVA_ID; exe=JavaBench.app/JavaBench else app="$CMP_APP"; id=$CMP_ID; exe=Keynote.app/Keynote; fi xcrun simctl uninstall "$DEVICE" "$id" 2>/dev/null || true xcrun simctl install "$DEVICE" "$app" @@ -160,8 +173,8 @@ perf() { j < NF && $(j + 1) !~ /^(perf-done|perf-start|first-frame)$/ { v[$(j + 1)] = v[$(j + 1)] (v[$(j + 1)] == "" ? "" : ",") $(j + 2) } END { for (k in v) print k "=" v[k] }' > "$WORK/$name-perf.txt" } -perf $CN1_ID cn1; perf $CMP_ID cmp -CN1_RSS="$(footprint_bytes $CN1_ID Bench.app/Bench)"; CMP_RSS="$(footprint_bytes $CMP_ID Keynote.app/Keynote)" +perf $CN1_ID cn1; perf $JAVA_ID java; perf $CMP_ID cmp +CN1_RSS="$(footprint_bytes $CN1_ID /Bench.app/Bench)"; JAVA_RSS="$(footprint_bytes $JAVA_ID JavaBench.app/JavaBench)"; CMP_RSS="$(footprint_bytes $CMP_ID Keynote.app/Keynote)" python3 - "$WORK" < Date: Wed, 28 Oct 2026 07:51:18 +0200 Subject: [PATCH 24/32] Safe-area snap re-marks its containers for layout; guide grammar; Painting buffers excluded from SpotBugs Suppressing the snap's PADDING event also dropped the setShouldCalcPreferredSize(true) it carried, so nested containers kept layouts made for the unsnapped padding. Do that part explicitly, still without the parent revalidateLater that caused the treadmill. Co-Authored-By: Claude Opus 5.5 --- CodenameOne/src/com/codename1/ui/Container.java | 7 +++++++ .../Kotlin-Multiplatform-Interop.asciidoc | 4 ++-- maven/compose-compat/spotbugs-exclude.xml | 10 ++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CodenameOne/src/com/codename1/ui/Container.java b/CodenameOne/src/com/codename1/ui/Container.java index 7e9a9e561c6..8df363a4f09 100644 --- a/CodenameOne/src/com/codename1/ui/Container.java +++ b/CodenameOne/src/com/codename1/ui/Container.java @@ -2972,6 +2972,13 @@ private boolean snapToSafeAreaInternal() { } } style.setSuppressChangeEvents(suppressEvents); + if (changed && !shouldCalcPreferredSize) { + // What the suppressed PADDING event did, minus its revalidateLater + // of the parent: this and every nested container measure and lay + // out again against the snapped padding. Without it a nested + // container kept a layout made for the unsnapped one. + setShouldCalcPreferredSize(true); + } return changed; } diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc index 00c7f86658f..f262f8a3770 100644 --- a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -80,8 +80,8 @@ pays; the rest of the application can stay in Compose. === One platform API on every target The `expect`/`actual` declarations a KMP project writes per platform are -mostly calls Codename One already makes the same everywhere. A composable -reaches each of them as ordinary Kotlin calls: +mostly calls Codename One already makes the same everywhere. Composable code +calls each of them as an ordinary Kotlin function: - <>, in-app purchase and subscriptions, and < + + + + + From 24e8bf99ac6b087bd42990572e967a6b338207ef Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 08:10:07 +0200 Subject: [PATCH 25/32] kmp-bench: Java list on InfiniteContainer with pooled cards; new numbers; guide credits KMP and explains the imperative gap Co-Authored-By: Claude Opus 5.5 --- .../Kotlin-Multiplatform-Interop.asciidoc | 48 +++++++---- .../developer-guide/img/kmp-bench-results.svg | 84 +++++++++---------- scripts/kmp-bench/README.md | 56 ++++++++----- scripts/kmp-bench/app/java/JavaBench.java | 70 ++++++++-------- scripts/kmp-bench/results.json | 77 +++++++++++++++++ 5 files changed, 222 insertions(+), 113 deletions(-) create mode 100644 scripts/kmp-bench/results.json diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc index f262f8a3770..53c88cbe397 100644 --- a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -10,6 +10,12 @@ watchOS and tvOS. === Motivation +Kotlin Multiplatform with Compose Multiplatform is the toolkit closest to +Codename One's own model: one codebase, one declarative UI, logic shared +between every client. It's a good way to write an application. Codename One +takes that code further -- onto more platforms, into a smaller and faster +application, and next to a Java API that every target shares. + Compose Multiplatform runs on Kotlin/Native on iOS and links its own drawing engine into the application. Codename One takes the JVM half of a KMP project instead. kotlinc already compiles `commonMain` to ordinary class files for @@ -26,14 +32,15 @@ the next section explains: image::img/kmp-bench-results.svg["Size, memory, start-up, recomposition and scrolling of the same application built by Codename One from Compose and from Java, relative to Compose Multiplatform",scaledwidth=95%] -- **Recomposition and scrolling take a quarter to a third of the CPU time.** - A state change is drawn in 5.3 ms against 20.4 ms, and one step of - scrolling in 5.6 ms against 16.9 ms. Every side keeps pace with the display, - so the difference is headroom: for heavier screens, and for battery. +- **The same Compose code runs in a quarter of the CPU time.** A state change + is drawn in 4.6 ms against 19.2 ms, and one step of scrolling in 4.0 ms + against 17.0 ms. Both keep pace with the display, so the difference is + headroom: for heavier screens, older devices and battery. - **The executable is 27% smaller** (16.8 MB against 22.9 MB) and the bundle 20% smaller, although the Codename One one carries the whole iOS port. -- **Start-up is faster**, 794 ms against 1051 ms cold. -- **Memory is level**, 48 MB of physical footprint against 49 MB. +- **Start-up is faster**, 961 ms against 1008 ms cold and 580 ms against + 604 ms warm. +- **Memory is level**, 47 MB of physical footprint against 48 MB. The benchmark, its method and how to run it are in `scripts/kmp-bench` in the Codename One repository. These are simulator numbers; they show where each @@ -64,18 +71,27 @@ language and a different UI toolkit on every platform. The benchmark measures what that floor is worth. `JavaBench.java` in `scripts/kmp-bench/app/java` is the Keynote screens written against Codename -One directly, timed by the same steps, and it ships no Kotlin standard +One directly, timed by the same steps. Its list is an `InfiniteContainer`, +which makes cards a batch at a time as the scroll nears the end -- as +`LazyColumn` composes only what comes into view -- and rebinds a card it has +already made instead of building another. It ships no Kotlin standard library, no Compose runtime and no coroutines: -- **The executable is 8.9 MB**, 39% of Compose Multiplatform's 22.9 MB and - about half of the Compose build's, and the bundle is 10.2 MB. -- **A step of scrolling takes 1.8 ms**, against 5.6 ms for the Compose build - and 16.9 ms for Compose Multiplatform. A day change takes 4.6 ms. -- **Memory is 53 MB**: it builds all fifty cards of a day as components, - where a lazy list composes only those in view. - -A hot list, a custom chart or a screen that has to fit a watch is where this -pays; the rest of the application can stay in Compose. +- **A state change is drawn in 1.5 ms**, thirteen times faster than Compose + Multiplatform's 19.2 ms and three times faster than the Compose build. +- **A step of scrolling takes 1.8 ms**, against 17.0 ms for Compose + Multiplatform. +- **The executable is 8.9 MB**, 39% of Compose Multiplatform's 22.9 MB, and + the bundle is 10.2 MB. +- **Memory is level** with both, at 47 MB. + +The gap is the price of the reactive model, not of Kotlin. A composition +records the state it read, compares what it emits against what it emitted +last time and applies the difference; an imperative update sets three labels +on a card that already exists. That bookkeeping is what makes Compose +pleasant to write, and on Codename One it's fast enough for any screen. A +hot list, a custom chart or a screen that has to fit a watch is where the +imperative path pays; the rest of the application can stay in Compose. === One platform API on every target diff --git a/docs/developer-guide/img/kmp-bench-results.svg b/docs/developer-guide/img/kmp-bench-results.svg index 3644a5c60c5..ca32ed2c4cb 100644 --- a/docs/developer-guide/img/kmp-bench-results.svg +++ b/docs/developer-guide/img/kmp-bench-results.svg @@ -7,55 +7,55 @@ Codename One, the same screens in Java Executable, stripped - - 73% (16.8 MB vs 22.9 MB) - - 39% (8.9 MB vs 22.9 MB) + + 73% (16.8 MB vs 22.9 MB) + + 39% (8.9 MB vs 22.9 MB) App bundle - - 80% (18.3 MB vs 22.9 MB) - - 44% (10.2 MB vs 22.9 MB) + + 80% (18.3 MB vs 22.9 MB) + + 45% (10.2 MB vs 22.9 MB) Physical footprint - - 98% (48.0 MB vs 49.0 MB) - - 108% (53.0 MB vs 49.0 MB) + + 98% (47.0 MB vs 48.0 MB) + + 98% (47.0 MB vs 48.0 MB) Cold start - - 76% (794 ms vs 1051 ms) - - 72% (754 ms vs 1051 ms) + + 95% (961 ms vs 1008 ms) + + 80% (805 ms vs 1008 ms) Warm start, median - - 93% (556 ms vs 598 ms) - - 94% (560 ms vs 598 ms) + + 96% (580 ms vs 604 ms) + + 95% (572 ms vs 604 ms) Recomposition, median - - 26% (5.3 ms vs 20 ms) - - 22% (4.6 ms vs 20 ms) + + 24% (4.6 ms vs 19 ms) + + 8% (1.5 ms vs 19 ms) Recomposition, p95 - - 29% (6.8 ms vs 23 ms) - - 25% (5.7 ms vs 23 ms) + + 27% (6.2 ms vs 23 ms) + + 9% (2.0 ms vs 23 ms) Scroll work per frame, median - - 33% (5.6 ms vs 17 ms) - - 11% (1.8 ms vs 17 ms) + + 24% (4.0 ms vs 17 ms) + + 10% (1.8 ms vs 17 ms) Scroll work per frame, p95 - - 43% (8.1 ms vs 19 ms) - - 10% (1.9 ms vs 19 ms) + + 36% (6.6 ms vs 18 ms) + + 15% (2.7 ms vs 18 ms) Scroll frame interval, median - - 119% (40 ms vs 33 ms) - - 114% (38 ms vs 33 ms) - - Compose Multiplatform = 100% + + 106% (35 ms vs 33 ms) + + 104% (35 ms vs 33 ms) + + Compose Multiplatform = 100% diff --git a/scripts/kmp-bench/README.md b/scripts/kmp-bench/README.md index 7c1f6aa73d4..da8b2e760e4 100644 --- a/scripts/kmp-bench/README.md +++ b/scripts/kmp-bench/README.md @@ -50,18 +50,18 @@ Start-up here is from process start (the kernel's start time) to the marker, so | | Codename One, Compose | Codename One, Java | Compose Multiplatform | |---|---|---|---| -| Cold start | 794 ms | 754 ms | 1051 ms | -| Warm start (median) | 555 ms | 560 ms | 597 ms | +| Cold start | 961 ms | 805 ms | 1008 ms | +| Warm start (median) | 580 ms | 572 ms | 604 ms | | Executable, stripped | 16.8 MB | 8.9 MB | 22.9 MB | | App bundle, executable stripped | 18.3 MB | 10.2 MB | 22.9 MB | | Bundle without executable | 1.3 MB | 1.3 MB | 2.5 KB | -| Physical footprint | 48.0 MB | 53.0 MB | 49.0 MB | -| Recomposition, day change to drawn (median) | 5.31 ms | 4.56 ms | 20.37 ms | -| Recomposition, day change to drawn (p95) | 6.79 ms | 5.67 ms | 23.08 ms | -| Scroll, work per frame (median) | 5.60 ms | 1.80 ms | 16.94 ms | -| Scroll, work per frame (p95) | 8.06 ms | 1.94 ms | 18.81 ms | -| Scroll, frame interval (median) | 39.52 ms | 37.91 ms | 33.34 ms | -| Scroll, frame interval (p95) | 45.00 ms | 43.98 ms | 35.05 ms | +| Physical footprint | 47.0 MB | 47.0 MB | 48.0 MB | +| Recomposition, day change to drawn (median) | 4.58 ms | 1.49 ms | 19.24 ms | +| Recomposition, day change to drawn (p95) | 6.17 ms | 2.01 ms | 23.02 ms | +| Scroll, work per frame (median) | 4.04 ms | 1.76 ms | 17.03 ms | +| Scroll, work per frame (p95) | 6.60 ms | 2.68 ms | 18.21 ms | +| Scroll, frame interval (median) | 35.45 ms | 34.79 ms | 33.35 ms | +| Scroll, frame interval (p95) | 38.12 ms | 37.94 ms | 34.74 ms | | Scroll, frames over 2x median | 0 of 144 | 0 of 144 | 0 of 144 | Runtime values are the median over 5 launches of each launch's median or p95. @@ -75,23 +75,37 @@ are linked in. The CN1 bundle holds 1.3 MB besides its executable: `iOSModernTheme.res` (213 KB), `default.metallib` (92 KB) and `kotlin.kotlin_builtins` (29 KB). -**Codename One does each step in a quarter to a third of the time.** A day -change takes 5.3 ms from state change to drawn on Codename One and 20.4 ms on -CMP; one card of scrolling takes 5.6 ms against 16.9 ms. Every side then waits +**Compose Multiplatform is the reference point, and a good one.** It is the +closest thing to Codename One's own model -- one codebase, one UI, shared +logic -- and it keeps pace with the display on every step here. The question +is how much CPU each runtime spends to get there. + +**Codename One runs the same Compose code in a quarter of the time.** A day +change takes 4.6 ms from state change to drawn on Codename One and 19.2 ms on +CMP; one card of scrolling takes 4.0 ms against 17.0 ms. Every side then waits for the display: `scrollToItem` is issued once per drawn frame, so the frame interval is about two display frames on each, and none dropped a frame against its own median. These are simulator numbers on an Apple-silicon Mac. They show where each runtime spends CPU in identical code. They do not show device frame rates, GPU cost, or how a finger-driven fling feels. -**The Java column is the same app without Kotlin.** `app/java/JavaBench.java` -is the Keynote screens written against Codename One directly -- the same -programme generator, the same cards, the same steps timed the same way, a 1 px -marker painted after the list -- so it ships no Kotlin standard library, no -Compose runtime and no coroutines. Its executable is 8.9 MB, 39% of CMP's and -about half of the Compose build; a scroll step is 1.8 ms. Memory is the one -row it does not win: it builds all fifty cards of a day as components, where a -lazy list composes only those in view. +**The Java column is the same app without Kotlin, and it is faster again.** +`app/java/JavaBench.java` is the Keynote screens written against Codename One +directly -- the same programme generator, the same cards, the same steps timed +the same way, a 1 px marker painted after the list -- so it ships no Kotlin +standard library, no Compose runtime and no coroutines. The list is an +`InfiniteContainer`: cards are made a batch at a time as the scroll nears the +end, as `LazyColumn` composes only what comes into view, and a card once made +is rebound rather than rebuilt. A day change is 1.5 ms, thirteen times faster +than CMP; a scroll step is 1.8 ms, nearly ten times faster; the executable is +8.9 MB, 39% of CMP's; memory is level with both. + +That gap is the cost of the reactive model, not of Kotlin's speed. A +composition records what it read, diffs what it emits against its slot table +and hands the changes to an applier. An imperative update sets three labels on +a card that already exists. Compose buys a great deal with that bookkeeping, +and on Codename One it is fast enough for any screen; the Java path is there +for the screens where the last few milliseconds, or megabytes, decide. Building it found a framework bug every Codename One app on a notched device paid: a Form with a Toolbar laid itself out again on every EDT cycle, forever, @@ -115,7 +129,7 @@ before quoting a difference. The Codename One executable includes the whole iOS port and the full Material icon font, so the 6.1 MB difference is a floor. -**Memory is level.** The Java heap holds +**Memory is level**, 47 MB on both Codename One builds against 48 MB. The Java heap holds 3.2 MB live after start-up, in about 57,000 objects. Loading the theme used to leave 24,000 strings behind -- one per key and value, plus every colour and padding built by concatenation, and a cache key per `Font.derive` call. The diff --git a/scripts/kmp-bench/app/java/JavaBench.java b/scripts/kmp-bench/app/java/JavaBench.java index 4784f82a9b8..5436a4d851f 100644 --- a/scripts/kmp-bench/app/java/JavaBench.java +++ b/scripts/kmp-bench/app/java/JavaBench.java @@ -32,6 +32,7 @@ import com.codename1.ui.FontImage; import com.codename1.ui.Form; import com.codename1.ui.Graphics; +import com.codename1.ui.InfiniteContainer; import com.codename1.ui.Label; import com.codename1.ui.Tabs; import com.codename1.ui.TextField; @@ -335,58 +336,59 @@ public void paintBackground(Graphics g) { } /** - * The scrolling list of cards. Cards are kept and rebound when the day or - * the query changes, as a lazy list reuses its item nodes. + * The scrolling list of cards: an InfiniteContainer, so cards are made a + * batch at a time as the scroll nears the end, as LazyColumn composes only + * what comes into view. A card, once made, is kept in a pool and rebound + * when the day or the query changes, as a lazy list reuses its item nodes. */ - static final class SessionList extends Container { + static final class SessionList extends InfiniteContainer { + private static final int BATCH = 8; private final JavaBench app; - private final List cards = new ArrayList(); + private final List pool = new ArrayList(); private final Label empty = new Label(); - private int shown; + private List items = new ArrayList(); SessionList(JavaBench app) { - super(BoxLayout.y()); + super(BATCH); this.app = app; - setScrollableY(true); getAllStyles().setPaddingUnit(Style.UNIT_TYPE_DIPS); getAllStyles().setPadding(1.3f, 1.3f, 2.5f, 2.5f); empty.getAllStyles().setAlignment(Component.CENTER); empty.getAllStyles().setFgColor(0x49454F); } - void show(List list, String emptyText) { - while (cards.size() < list.size()) { - Card c = new Card(app); - cards.add(c); - add(c); - } - for (int i = 0; i < cards.size(); i++) { - Card c = cards.get(i); - boolean visible = i < list.size(); - if (visible) { - c.bind(list.get(i)); - } - c.setHidden(!visible); - c.setVisible(visible); + @Override + public Component[] fetchComponents(int index, int amount) { + if (items.isEmpty()) { + return index == 0 ? new Component[] {empty} : new Component[0]; } - if (list.isEmpty()) { - empty.setText(emptyText); - if (empty.getParent() == null) { - add(empty); + int n = Math.max(0, Math.min(amount, items.size() - index)); + Component[] out = new Component[n]; + for (int i = 0; i < n; i++) { + while (pool.size() <= index + i) { + pool.add(new Card(app)); } - } else if (empty.getParent() != null) { - removeComponent(empty); - } - if (shown != list.size()) { - shown = list.size(); - revalidate(); + Card c = pool.get(index + i); + c.bind(items.get(index + i)); + out[i] = c; } - repaint(); + return out; + } + + void show(List list, String emptyText) { + items = list; + empty.setText(emptyText); + refresh(); } - /** The list's scrollToItem: card i at the top. */ + /** The list's scrollToItem: card i at the top, made first if needed. */ void scrollToCard(int i) { - int y = cards.get(i).getY() - getStyle().getPaddingTop(); + int y = 0; + if (i < pool.size() && pool.get(i).getParent() == this) { + y = pool.get(i).getY() - getStyle().getPaddingTop(); + } else { + y = Integer.MAX_VALUE; + } int max = Math.max(0, getScrollDimension().getHeight() - getHeight()); setScrollY(Math.max(0, Math.min(y, max))); } diff --git a/scripts/kmp-bench/results.json b/scripts/kmp-bench/results.json new file mode 100644 index 00000000000..3e9bb61fec7 --- /dev/null +++ b/scripts/kmp-bench/results.json @@ -0,0 +1,77 @@ +{ + "codename_one": { + "cold_start_ms": 961, + "warm_start_median_ms": 580.0, + "starts_ms": [ + 961, + 621, + 592, + 566, + 568 + ], + "executable_stripped_bytes": 17617384, + "bundle_bytes": 19152896, + "bundle_without_executable_bytes": 1405874, + "footprint_bytes": 49283072, + "perf_median_of_runs_us": { + "recompose-p95-us": 6173, + "scroll-work-p95-us": 6601, + "recompose-median-us": 4576, + "scroll-frames": 144, + "scroll-work-median-us": 4039, + "scroll-frame-p95-us": 38122, + "scroll-frame-median-us": 35447, + "scroll-jank-frames": 0 + } + }, + "codename_one_java": { + "cold_start_ms": 805, + "warm_start_median_ms": 572.0, + "starts_ms": [ + 805, + 663, + 567, + 573, + 571 + ], + "executable_stripped_bytes": 9354120, + "bundle_bytes": 10731520, + "bundle_without_executable_bytes": 1324542, + "footprint_bytes": 49283072, + "perf_median_of_runs_us": { + "recompose-p95-us": 2013, + "scroll-work-p95-us": 2677, + "recompose-median-us": 1487, + "scroll-frames": 144, + "scroll-work-median-us": 1759, + "scroll-frame-p95-us": 37944, + "scroll-frame-median-us": 34789, + "scroll-jank-frames": 0 + } + }, + "compose_multiplatform": { + "cold_start_ms": 1008, + "warm_start_median_ms": 604.0, + "starts_ms": [ + 1008, + 713, + 597, + 603, + 605 + ], + "executable_stripped_bytes": 24041184, + "bundle_bytes": 24051712, + "bundle_without_executable_bytes": 2510, + "footprint_bytes": 50331648, + "perf_median_of_runs_us": { + "recompose-p95-us": 23015, + "scroll-work-p95-us": 18207, + "recompose-median-us": 19238, + "scroll-frames": 144, + "scroll-work-median-us": 17030, + "scroll-frame-p95-us": 34740, + "scroll-frame-median-us": 33349, + "scroll-jank-frames": 0 + } + } +} \ No newline at end of file From e28b8cef413ca76b1e68f4101e2205eb4a9bae3f Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 08:17:02 +0200 Subject: [PATCH 26/32] Guide: the session card in Compose and in Java side by side, and a plain unit test for the Java one Co-Authored-By: Claude Opus 5.5 --- .../Kotlin-Multiplatform-Interop.asciidoc | 97 ++++++++++++++++++- scripts/kmp-bench/README.md | 7 +- 2 files changed, 97 insertions(+), 7 deletions(-) diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc index 53c88cbe397..bc701dca9b1 100644 --- a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -88,10 +88,99 @@ library, no Compose runtime and no coroutines: The gap is the price of the reactive model, not of Kotlin. A composition records the state it read, compares what it emits against what it emitted last time and applies the difference; an imperative update sets three labels -on a card that already exists. That bookkeeping is what makes Compose -pleasant to write, and on Codename One it's fast enough for any screen. A -hot list, a custom chart or a screen that has to fit a watch is where the -imperative path pays; the rest of the application can stay in Compose. +on a card that already exists. + +Nor does the declarative form buy readability. Here is one session card both +ways. The Compose version, from the benchmark: + +[source,kotlin] +---- +@Composable +fun SessionCard(s: Session, favorite: Boolean, onFavorite: () -> Unit, onOpen: () -> Unit) { + Card(onOpen, Modifier.fillMaxWidth().testTag("session-${s.id}")) { + Row(verticalAlignment = Alignment.CenterVertically) { + Box(Modifier.width(6.dp).height(84.dp).background(Color(s.track.color))) + Column(Modifier.weight(1f).padding(12.dp)) { + Text(s.timeLabel + " - " + s.room, style = MaterialTheme.typography.labelMedium) + Text(s.title, style = MaterialTheme.typography.titleMedium, maxLines = 2) + Text(s.speaker.name + ", " + s.speaker.company, + style = MaterialTheme.typography.bodySmall) + } + FavoriteButton(favorite, onFavorite, Modifier.testTag("fav-${s.id}")) + } + } +} +---- + +And in Codename One, with the look in the theme's CSS where a designer can +change it: + +[source,java] +---- +public class SessionCard extends Container { + final Label time = new Label("", "SessionTime"); + final Label title = new Label("", "SessionTitle"); + final Label speaker = new Label("", "SessionSpeaker"); + final Button heart = new Button("", "Favorite"); + private Session session; + + public SessionCard(Favorites favorites) { + super(new BorderLayout(), "SessionCard"); + add(BorderLayout.CENTER, BoxLayout.encloseY(time, title, speaker)); + add(BorderLayout.EAST, heart); + heart.addActionListener(e -> { + favorites.toggle(session); + bind(session, favorites); + }); + } + + public void bind(Session s, Favorites favorites) { + session = s; + time.setText(s.getTimeLabel() + " - " + s.getRoom()); + title.setText(s.getTitle()); + speaker.setText(s.getSpeaker() + ", " + s.getCompany()); + FontImage.setMaterialIcon(heart, favorites.contains(s) + ? FontImage.MATERIAL_FAVORITE : FontImage.MATERIAL_FAVORITE_BORDER); + } +} +---- + +The Java card says what happens and when: the components are made once, +`bind` fills them, a tap changes the favourite and shows it. Nothing reruns +behind the code's back, so a breakpoint in `bind` stops exactly when a card +shows a session -- the question a recomposition count leaves open. + +It tests as directly. A component is an ordinary object, so a unit test makes +one, binds it and presses its button, with no test rule, semantics tree or +tags kept only for the test: + +[source,java] +---- +public class SessionCardTest extends AbstractTest { + @Override + public boolean runTest() throws Exception { + Favorites favorites = new Favorites(); + Session talk = new Session(7, "Keynote", "A. Speaker", "Codename One", + "Hall A", "09:00"); + SessionCard card = new SessionCard(favorites); + card.bind(talk, favorites); + + assertEqual("Keynote", card.title.getText()); + assertFalse(favorites.contains(talk)); + + card.heart.released(); // a tap + assertTrue(favorites.contains(talk), "the heart marks the session"); + return true; + } +} +---- + +The test sits in the card's package, so it reads the fields directly. It +runs in the simulator with `mvn cn1:test`, like every other Codename One test. + +A hot list, a custom chart or a screen that has to fit a watch is where the +imperative path pays first; nothing stops a team writing every screen that +way. === One platform API on every target diff --git a/scripts/kmp-bench/README.md b/scripts/kmp-bench/README.md index da8b2e760e4..b1bff3bd9bb 100644 --- a/scripts/kmp-bench/README.md +++ b/scripts/kmp-bench/README.md @@ -103,9 +103,10 @@ than CMP; a scroll step is 1.8 ms, nearly ten times faster; the executable is That gap is the cost of the reactive model, not of Kotlin's speed. A composition records what it read, diffs what it emits against its slot table and hands the changes to an applier. An imperative update sets three labels on -a card that already exists. Compose buys a great deal with that bookkeeping, -and on Codename One it is fast enough for any screen; the Java path is there -for the screens where the last few milliseconds, or megabytes, decide. +a card that already exists, and reads as plainly: the components are made +once, `bind` fills them, and a test makes a card and presses its button +without a test rule or semantics tree. The developer guide's Kotlin +Multiplatform chapter shows the card both ways and a test for it. Building it found a framework bug every Codename One app on a notched device paid: a Form with a Toolbar laid itself out again on every EDT cycle, forever, From a60b27c2e9602cc8a0b6b69e65c472310360afd8 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 08:24:31 +0200 Subject: [PATCH 27/32] JavaSE FFmpeg video: each decoded frame refreshes the peer buffer and repaints it The form's endless safe-area relayout used to do this by accident; with that gone, the video peer showed no frames. Co-Authored-By: Claude Opus 5.5 --- .../impl/javase/ffmpeg/FFMPEGMedia.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/ffmpeg/FFMPEGMedia.java b/Ports/JavaSE/src/com/codename1/impl/javase/ffmpeg/FFMPEGMedia.java index 670ca89804e..d1f517a4f8b 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/ffmpeg/FFMPEGMedia.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/ffmpeg/FFMPEGMedia.java @@ -51,6 +51,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.FloatControl; @@ -321,11 +323,14 @@ public Component getVideoComponent() { } if (SwingUtilities.isEventDispatchThread()) { videoComponent = new JavaSEPort.Peer(frame, panel); + panel.attach(videoComponent); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - videoComponent = new JavaSEPort.Peer(frame, panel); + JavaSEPort.Peer created = new JavaSEPort.Peer(frame, panel); + panel.attach(created); + videoComponent = created; } }); CN.invokeAndBlock(new Runnable() { @@ -706,6 +711,8 @@ private static class ProbeData { private static class VideoPanel extends JPanel implements HierarchyListener { private volatile BufferedImage image; private volatile int frameCount; + private final AtomicReference peer = new AtomicReference(); + private final AtomicBoolean framePending = new AtomicBoolean(); VideoPanel(int width, int height) { setBackground(Color.BLACK); @@ -730,6 +737,38 @@ void updateFrame(byte[] bytes, int width, int height) { image = next; frameCount++; repaint(); + showFrameInPeer(); + } + + void attach(JavaSEPort.Peer p) { + peer.set(p); + } + + // A new frame repaints this Swing panel, but the Codename One form + // draws the peer from a buffer copied out of it, and nothing else + // asks for that copy or for the form to repaint. It used to work by + // accident: a safe-area form relaid itself out every EDT cycle, which + // repainted the peer, which refreshed the buffer. Each frame now + // copies the buffer on the AWT thread and repaints the peer on the + // EDT itself, coalesced so a fast decoder queues at most one of each. + private void showFrameInPeer() { + final JavaSEPort.Peer p = peer.get(); + if (p == null || !framePending.compareAndSet(false, true)) { + return; + } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + framePending.set(false); + p.paintOnBuffer(); + CN.callSerially(new Runnable() { + @Override + public void run() { + p.repaint(); + } + }); + } + }); } boolean hasFrame() { From 4e51e2243bb481e9f63666a601d46b85eb042614 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 10:35:18 +0200 Subject: [PATCH 28/32] Guide's session card snippets compiled from demo sources; Compose's Gradle-provided libraries on the 16 KB list The side-by-side Compose and Java cards and the test now live in compiled sources the guide includes by tag, and the Java card is self-contained so the Playground runs it. kotlinx-coroutines-core, androidx.collection and androidx.annotation are declared by the Android builder for Compose apps, so the 16 KB alignment check resolves them too. Co-Authored-By: Claude Opus 5.5 --- .../developerguide/kmp/SessionCard.java | 54 +++++++++++++++ .../developerguide/SessionCardTest.java | 43 ++++++++++++ .../Kotlin-Multiplatform-Interop.asciidoc | 68 ++----------------- .../AndroidDependencyCoordinates.java | 4 ++ .../hellocodenameone/kmp/GuideSessionCard.kt | 62 +++++++++++++++++ 5 files changed, 170 insertions(+), 61 deletions(-) create mode 100644 docs/demos/common/src/main/java/com/codenameone/developerguide/kmp/SessionCard.java create mode 100644 docs/demos/common/src/test/java/com/codenameone/developerguide/SessionCardTest.java create mode 100644 scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/kmp/SessionCard.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/kmp/SessionCard.java new file mode 100644 index 00000000000..80a271b2d9f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/kmp/SessionCard.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.developerguide.kmp; + +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +// tag::kotlin-multiplatform-interop-session-card[] +public class SessionCard extends Container { + public final Label time = new Label("", "SessionTime"); + public final Label title = new Label("", "SessionTitle"); + public final Label speaker = new Label("", "SessionSpeaker"); + public final Button heart = new Button("", "Favorite"); + + public SessionCard(Runnable onFavorite) { + super(new BorderLayout(), "SessionCard"); + add(BorderLayout.CENTER, BoxLayout.encloseY(time, title, speaker)); + add(BorderLayout.EAST, heart); + heart.addActionListener(e -> onFavorite.run()); + } + + public void bind(String when, String talk, String by, boolean favorite) { + time.setText(when); + title.setText(talk); + speaker.setText(by); + FontImage.setMaterialIcon(heart, favorite + ? FontImage.MATERIAL_FAVORITE : FontImage.MATERIAL_FAVORITE_BORDER); + } +} +// end::kotlin-multiplatform-interop-session-card[] diff --git a/docs/demos/common/src/test/java/com/codenameone/developerguide/SessionCardTest.java b/docs/demos/common/src/test/java/com/codenameone/developerguide/SessionCardTest.java new file mode 100644 index 00000000000..3cdebf8b60d --- /dev/null +++ b/docs/demos/common/src/test/java/com/codenameone/developerguide/SessionCardTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.developerguide; + +// tag::kotlin-multiplatform-interop-session-card-test[] +import com.codename1.testing.AbstractTest; +import com.codenameone.developerguide.kmp.SessionCard; + +public class SessionCardTest extends AbstractTest { + @Override + public boolean runTest() throws Exception { + int[] taps = {0}; + SessionCard card = new SessionCard(() -> taps[0]++); + card.bind("09:00 - Hall A", "Keynote", "A. Speaker, Codename One", false); + + assertEqual("Keynote", card.title.getText()); + + card.heart.released(); // a tap + assertTrue(taps[0] == 1, "the heart reports the tap"); + return true; + } +} +// end::kotlin-multiplatform-interop-session-card-test[] diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc index bc701dca9b1..f17e734636b 100644 --- a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -95,21 +95,7 @@ ways. The Compose version, from the benchmark: [source,kotlin] ---- -@Composable -fun SessionCard(s: Session, favorite: Boolean, onFavorite: () -> Unit, onOpen: () -> Unit) { - Card(onOpen, Modifier.fillMaxWidth().testTag("session-${s.id}")) { - Row(verticalAlignment = Alignment.CenterVertically) { - Box(Modifier.width(6.dp).height(84.dp).background(Color(s.track.color))) - Column(Modifier.weight(1f).padding(12.dp)) { - Text(s.timeLabel + " - " + s.room, style = MaterialTheme.typography.labelMedium) - Text(s.title, style = MaterialTheme.typography.titleMedium, maxLines = 2) - Text(s.speaker.name + ", " + s.speaker.company, - style = MaterialTheme.typography.bodySmall) - } - FavoriteButton(favorite, onFavorite, Modifier.testTag("fav-${s.id}")) - } - } -} +include::../../scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt[tag=kotlin-multiplatform-interop-session-card-compose,indent=0] ---- And in Codename One, with the look in the theme's CSS where a designer can @@ -117,36 +103,11 @@ change it: [source,java] ---- -public class SessionCard extends Container { - final Label time = new Label("", "SessionTime"); - final Label title = new Label("", "SessionTitle"); - final Label speaker = new Label("", "SessionSpeaker"); - final Button heart = new Button("", "Favorite"); - private Session session; - - public SessionCard(Favorites favorites) { - super(new BorderLayout(), "SessionCard"); - add(BorderLayout.CENTER, BoxLayout.encloseY(time, title, speaker)); - add(BorderLayout.EAST, heart); - heart.addActionListener(e -> { - favorites.toggle(session); - bind(session, favorites); - }); - } - - public void bind(Session s, Favorites favorites) { - session = s; - time.setText(s.getTimeLabel() + " - " + s.getRoom()); - title.setText(s.getTitle()); - speaker.setText(s.getSpeaker() + ", " + s.getCompany()); - FontImage.setMaterialIcon(heart, favorites.contains(s) - ? FontImage.MATERIAL_FAVORITE : FontImage.MATERIAL_FAVORITE_BORDER); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/kmp/SessionCard.java[tag=kotlin-multiplatform-interop-session-card,indent=0] ---- The Java card says what happens and when: the components are made once, -`bind` fills them, a tap changes the favourite and shows it. Nothing reruns +`bind` fills them, a tap calls back as `onFavorite` does. Nothing reruns behind the code's back, so a breakpoint in `bind` stops exactly when a card shows a session -- the question a recomposition count leaves open. @@ -154,28 +115,13 @@ It tests as directly. A component is an ordinary object, so a unit test makes one, binds it and presses its button, with no test rule, semantics tree or tags kept only for the test: -[source,java] +[source,java,role=cn1-no-try] ---- -public class SessionCardTest extends AbstractTest { - @Override - public boolean runTest() throws Exception { - Favorites favorites = new Favorites(); - Session talk = new Session(7, "Keynote", "A. Speaker", "Codename One", - "Hall A", "09:00"); - SessionCard card = new SessionCard(favorites); - card.bind(talk, favorites); - - assertEqual("Keynote", card.title.getText()); - assertFalse(favorites.contains(talk)); - - card.heart.released(); // a tap - assertTrue(favorites.contains(talk), "the heart marks the session"); - return true; - } -} +include::../demos/common/src/test/java/com/codenameone/developerguide/SessionCardTest.java[tag=kotlin-multiplatform-interop-session-card-test,indent=0] ---- -The test sits in the card's package, so it reads the fields directly. It +The card's components are public final fields, so the test reads them +directly. It runs in the simulator with `mvn cn1:test`, like every other Codename One test. A hot list, a custom chart or a screen that has to fit a watch is where the diff --git a/maven/build-engine/src/test/java/com/codename1/builders/AndroidDependencyCoordinates.java b/maven/build-engine/src/test/java/com/codename1/builders/AndroidDependencyCoordinates.java index 1bbf17282a1..9a1502ae1c7 100644 --- a/maven/build-engine/src/test/java/com/codename1/builders/AndroidDependencyCoordinates.java +++ b/maven/build-engine/src/test/java/com/codename1/builders/AndroidDependencyCoordinates.java @@ -83,10 +83,13 @@ public final class AndroidDependencyCoordinates { static final Map NOT_SHIPPED = new LinkedHashMap(); static { + // GRADLE_PROVIDED_LIBRARIES: a Compose application's copies, declared instead. + INJECTED.put("androidx.annotation:annotation", "1.9.1"); INJECTED.put("androidx.appcompat:appcompat", "1.6.1"); INJECTED.put("androidx.browser:browser", "1.8.0"); INJECTED.put("androidx.car.app:app", "1.4.0"); INJECTED.put("androidx.car.app:app-projected", "1.4.0"); + INJECTED.put("androidx.collection:collection", "1.5.0"); INJECTED.put("androidx.concurrent:concurrent-futures", "1.1.0"); INJECTED.put("androidx.credentials:credentials", "1.3.0"); INJECTED.put("androidx.credentials:credentials-play-services-auth", "1.3.0"); @@ -117,6 +120,7 @@ public final class AndroidDependencyCoordinates { INJECTED.put("com.huawei.hms:push", "6.3.0.302"); INJECTED.put("com.scottyab:rootbeer-lib", "0.1.2"); INJECTED.put("org.jetbrains.kotlin:kotlin-stdlib", "1.9.22"); + INJECTED.put("org.jetbrains.kotlinx:kotlinx-coroutines-core", "1.10.2"); INJECTED.put("org.unifiedpush.android:connector", "3.3.5"); NOT_SHIPPED.put("com.android.tools.build:gradle", "the Android Gradle plugin"); diff --git a/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt b/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt new file mode 100644 index 00000000000..dcf5356ccf7 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.kmp +package com.codenameone.examples.hellocodenameone.kmp + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Favorite +import androidx.compose.material.icons.filled.FavoriteBorder +import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +// The developer guide shows this card beside its Codename One twin, +// docs/demos/.../kmp/SessionCard.java; keep the two doing the same thing. +// tag::kotlin-multiplatform-interop-session-card-compose[] +@Composable +fun SessionCard(time: String, title: String, speaker: String, + favorite: Boolean, onFavorite: () -> Unit) { + Card(Modifier.fillMaxWidth()) { + Row(verticalAlignment = Alignment.CenterVertically) { + Column(Modifier.weight(1f).padding(12.dp)) { + Text(time, style = MaterialTheme.typography.labelMedium) + Text(title, style = MaterialTheme.typography.titleMedium) + Text(speaker, style = MaterialTheme.typography.bodySmall) + } + IconButton(onFavorite) { + Icon(if (favorite) Icons.Default.Favorite else Icons.Default.FavoriteBorder, null) + } + } + } +} +// end::kotlin-multiplatform-interop-session-card-compose[] From b366b853876162ec6661f9d1cafcac64c726be37 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 14:25:10 +0200 Subject: [PATCH 29/32] Size gap is Kotlin, time gap the reactive model; AddressBook links with NSContactsUsageDescription alone Co-Authored-By: Claude Opus 5.5 --- .../Kotlin-Multiplatform-Interop.asciidoc | 5 +++-- .../main/java/com/codename1/builders/IPhoneBuilder.java | 9 +++------ scripts/kmp-bench/README.md | 9 +++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc index f17e734636b..25edf36286f 100644 --- a/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc +++ b/docs/developer-guide/Kotlin-Multiplatform-Interop.asciidoc @@ -85,10 +85,11 @@ library, no Compose runtime and no coroutines: the bundle is 10.2 MB. - **Memory is level** with both, at 47 MB. -The gap is the price of the reactive model, not of Kotlin. A composition +The time gap is mostly the price of the reactive model. A composition records the state it read, compares what it emits against what it emitted last time and applies the difference; an imperative update sets three labels -on a card that already exists. +on a card that already exists. The size gap is Kotlin: the Java build carries +neither the Kotlin standard library nor the Compose runtime. Nor does the declarative form buy readability. Here is one session card both ways. The Compose version, from the benchmark: diff --git a/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java index 5d9bb3ff129..e68120f3fdf 100644 --- a/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java +++ b/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java @@ -4860,12 +4860,9 @@ public void usesClassMethod(String cls, String method) { } } - // The legacy AddressBook natives compile only under - // INCLUDE_CONTACTS_USAGE, which the usage-description block above - // turns on (every define, before Xcode 9). The frameworks go with - // the define: linked unconditionally they were loaded by every - // application at launch, and called by none without it. - if (xcodeVersion < 9 || privacyUsageDescriptions.containsKey("NSContactsUsageDescription")) { + // An application that calls the contacts API must declare + // NSContactsUsageDescription, so that is when AddressBook is linked. + if (privacyUsageDescriptions.containsKey("NSContactsUsageDescription")) { addLibs = appendFrameworks(addLibs, "AddressBookUI.framework", "AddressBook.framework"); } diff --git a/scripts/kmp-bench/README.md b/scripts/kmp-bench/README.md index b1bff3bd9bb..c209b26dacd 100644 --- a/scripts/kmp-bench/README.md +++ b/scripts/kmp-bench/README.md @@ -100,10 +100,11 @@ is rebound rather than rebuilt. A day change is 1.5 ms, thirteen times faster than CMP; a scroll step is 1.8 ms, nearly ten times faster; the executable is 8.9 MB, 39% of CMP's; memory is level with both. -That gap is the cost of the reactive model, not of Kotlin's speed. A -composition records what it read, diffs what it emits against its slot table -and hands the changes to an applier. An imperative update sets three labels on -a card that already exists, and reads as plainly: the components are made +The time gap is mostly the cost of the reactive model. A composition records +what it read, diffs what it emits against its slot table and hands the changes +to an applier. An imperative update sets three labels on a card that already +exists. The size gap is Kotlin: the Java build carries neither the Kotlin +standard library nor the Compose runtime. The Java card also reads as plainly: the components are made once, `bind` fills them, and a test makes a card and presses its button without a test rule or semantics tree. The developer guide's Kotlin Multiplatform chapter shows the card both ways and a test for it. From 3d30e96cdb0b2a96599653a3bc7fbcdb1bc0f58e Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 14:39:37 +0200 Subject: [PATCH 30/32] iOS: the contacts scan, not the usage description, decides AddressBook; a generic description when the app gives none The builder now asks ContactsPermissionScan, the scan the Android builder already uses, whether the application reaches the address book. When it does, AddressBook is linked and, if no NSContactsUsageDescription was declared, a generic one is supplied, which also turns on the define the natives compile under. Co-Authored-By: Claude Opus 5.5 --- .../com/codename1/builders/IPhoneBuilder.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java index e68120f3fdf..15030691136 100644 --- a/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java +++ b/maven/build-engine/src/main/java/com/codename1/builders/IPhoneBuilder.java @@ -1511,6 +1511,14 @@ boolean usesWearable() { * description.

*/ private boolean usesContactPicker; + + /** + * Whether the application reads or writes the address book, decided by + * the scan the Android builder asks the same question with. Such an + * application gets AddressBook linked and, when it declares none, a + * generic {@code NSContactsUsageDescription}. + */ + private final ContactsPermissionScan contactsScan = new ContactsPermissionScan(); private boolean usesCalendarEventApi; private boolean usesCalendarTaskApi; private String firstBonjourType; @@ -2757,6 +2765,7 @@ public void usesClass(String cls) { && "com/codename1/contacts/ContactPicker".equals(cls)) { usesContactPicker = true; } + contactsScan.usesClass(cls); if (!usesLocalNotifications && cls.indexOf("com/codename1/notifications/LocalNotification") == 0) { usesLocalNotifications = true; } @@ -3123,6 +3132,7 @@ public void usesClassMethod(String cls, String method) { || method.indexOf("isContactPickerSupported") > -1)) { usesContactPicker = true; } + contactsScan.usesClassMethod(cls, method); // Health.getStore()/getWorkouts() mean a real platform // store; Health.getSensors() means BLE only. The class // reference alone cannot tell them apart, so the facade @@ -3788,6 +3798,17 @@ public void usesClassMethod(String cls, String method) { } } + boolean usesContactsApi = contactsScan.readPermissionRequired() + || contactsScan.writePermissionRequired(); + if (usesContactsApi) { + // iOS terminates an app that opens the address book with no purpose + // string, so one the developer did not write is supplied. + String contacts = privacyUsageDescriptions.get("NSContactsUsageDescription"); + if (contacts == null || contacts.trim().length() == 0) { + privacyUsageDescriptions.put("NSContactsUsageDescription", + "Some functionality of the application requires access to your contacts."); + } + } if (xcodeVersion >= 9) { try { for (String privacyKey : privacyUsageDescriptions.keySet()) { @@ -4860,9 +4881,12 @@ public void usesClassMethod(String cls, String method) { } } - // An application that calls the contacts API must declare - // NSContactsUsageDescription, so that is when AddressBook is linked. - if (privacyUsageDescriptions.containsKey("NSContactsUsageDescription")) { + // Linked with the define the AddressBook natives compile under, + // INCLUDE_CONTACTS_USAGE, which the contacts usage description turns + // on; the scan supplies that description for an app that calls the + // contacts API without one. + if (contactsScan.readPermissionRequired() || contactsScan.writePermissionRequired() + || privacyUsageDescriptions.containsKey("NSContactsUsageDescription")) { addLibs = appendFrameworks(addLibs, "AddressBookUI.framework", "AddressBook.framework"); } From 4949b0335f5c0269d0fac0dc9c674527956f2511 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:31:27 +0200 Subject: [PATCH 31/32] GuideSessionCard.kt: drop the duplicated package line Co-Authored-By: Claude Opus 5.5 --- .../examples/hellocodenameone/kmp/GuideSessionCard.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt b/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt index dcf5356ccf7..109c595d5e7 100644 --- a/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt +++ b/scripts/hellocodenameone/common/src/main/kmp/jvmMain/kotlin/com/codenameone/examples/hellocodenameone/kmp/GuideSessionCard.kt @@ -21,7 +21,6 @@ * need additional information or have any questions. */ package com.codenameone.examples.hellocodenameone.kmp -package com.codenameone.examples.hellocodenameone.kmp import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row From 7a49d95f108b6653e4d1cb20bf21460ffee93a13 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Oct 2026 21:29:31 +0200 Subject: [PATCH 32/32] Pacing test: 32MB slack over the headroom margin; readings land ~8MB above it and 8MB failed by 8KB Co-Authored-By: Claude Opus 5.5 --- .../tools/translator/ProcessBudgetPacingIntegrationTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/ProcessBudgetPacingIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/ProcessBudgetPacingIntegrationTest.java index ee76c4f44b7..ee3950525ad 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/ProcessBudgetPacingIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/ProcessBudgetPacingIntegrationTest.java @@ -178,8 +178,11 @@ class ProcessBudgetPacingIntegrationTest { * Slack over the margin for the headroom assertion. Generous, because the assertion's * job is to separate a budget-derived figure from a host-wide one -- two readings * orders of magnitude apart -- not to pin down where exactly headroom bottomed out. + * The low point is sampled once per admitted block, after the collector has run, and + * on Linux runners it lands about 8MB above the margin: 72992KB in one build and 73736KB + * in the next, which an 8MB slack failed on by 8KB. */ - private static final long HEADROOM_MARGIN_SLACK_KB = 8 * 1024; + private static final long HEADROOM_MARGIN_SLACK_KB = 32 * 1024; /** * Bound on a single translated-binary run. The workload takes a few seconds; this is