[
  {
    "path": ".github/actions/get-avd-info/action.yml",
    "content": "# From: coil-kt/coil\nname: 'Get AVD Info'\ndescription: 'Get the AVD info based on its API level.'\ninputs:\n  api-level:\n    required: true\noutputs:\n  arch:\n    value: ${{ steps.get-avd-arch.outputs.arch }}\n  target:\n    value: ${{ steps.get-avd-target.outputs.target }}\nruns:\n  using: \"composite\"\n  steps:\n    - id: get-avd-arch\n      run: echo \"::set-output name=arch::$(if [ ${{ inputs.api-level }} -ge 30 ]; then echo x86_64; else echo x86; fi)\"\n      shell: bash\n    - id: get-avd-target\n      run: echo \"::set-output name=target::$(echo default)\"\n      shell: bash"
  },
  {
    "path": ".github/workflows/android.yml",
    "content": "name: Android CI\n\non:\n  push:\n    branches: [ \"main\" ]\n    paths-ignore:\n      - '**.md'\n  pull_request:\n    branches: [ \"main\" ]\n    paths-ignore:\n      - '**.md'\n\nconcurrency:\n  group: android-ci-${{ github.ref }}\n  cancel-in-progress: true\n\npermissions:\n  contents: read\n  checks: write\n  id-token: write\n\nenv:\n  JAVA_VERSION: \"17\"\n  JAVA_DISTR: 'zulu'\n\njobs:\n  tests_and_apk:\n    name: \"🤖 Local Tests and 📦 APKs\"\n    runs-on: ubuntu-20.04\n\n    timeout-minutes: 60\n\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v4\n\n      - name: Validate Gradle Wrapper\n        uses: gradle/actions/wrapper-validation@v3\n\n      - name: Set up JDK\n        uses: actions/setup-java@v4\n        with:\n          distribution: ${{ env.JAVA_DISTR }}\n          java-version: ${{ env.JAVA_VERSION }}\n\n      - name: Create google-services.json file\n        run: cat /home/runner/work/newquiz/newquiz/app/google-services.json | base64\n\n      - name: Put google-services.json data\n        env:\n          DATA: ${{ secrets.GOOGLE_SERVICES_JSON }}\n        run: echo $DATA > /home/runner/work/newquiz/newquiz/app/google-services.json\n\n      - name: Workaround for Android Gradle Plugin issue\n        run: 'echo \"ndk.dir=${ANDROID_HOME}/ndk-bundle\" > local.properties'\n\n      - name: Setup gradle\n        uses: gradle/actions/setup-gradle@v3\n\n      - name: Run unit tests\n        run: ./gradlew testAllUnitTest --stacktrace\n\n      - name: Publish Test Report\n        uses: mikepenz/action-junit-report@v4\n        if: success() || failure() # always run even if the previous step fails\n        with:\n          report_paths: '**/build/test-results/test*/TEST-*.xml'\n\n      - name: Lint sources\n        run: ./gradlew lint --stacktrace\n\n      - name: Generate GitHub annotations\n        uses: yutailang0119/action-android-lint@v4\n        with:\n          report-path: '**/build/reports/*.xml'\n\n      - name: Run Detekt\n        run: ./gradlew detekt --stacktrace\n\n      - name: Assemble debug APKs\n        run: ./gradlew assembleNormalDebug assembleFossDebug --stacktrace\n\n      - name: Upload APKs\n        uses: actions/upload-artifact@v4\n        with:\n          name: APKs\n          path: app/build/outputs/apk/**/app-*-universal-debug.apk\n"
  },
  {
    "path": ".github/workflows/android_old.yml",
    "content": "name: Android CI\n\non:\n  workflow_dispatch:\n\npermissions:\n  contents: read\n  checks: write\n  id-token: write\n\nenv:\n  JAVA_VERSION: \"17\"\n  JAVA_DISTR: 'corretto'\n\njobs:\n  test:\n    name: \"🤖 Unit Tests\"\n    runs-on: ubuntu-20.04\n\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v3\n\n      - name: Set up JDK\n        uses: actions/setup-java@v3\n        with:\n          distribution: ${{ env.JAVA_DISTR }}\n          java-version: ${{ env.JAVA_VERSION }}\n\n      - name: Create google-services.json file\n        run: cat /home/runner/work/newquiz/newquiz/app/google-services.json | base64\n\n      - name: Put google-services.json data\n        env:\n          DATA: ${{ secrets.GOOGLE_SERVICES_JSON }}\n        run: echo $DATA > /home/runner/work/newquiz/newquiz/app/google-services.json\n\n      - name: Workaround for Android Gradle Plugin issue\n        run: 'echo \"ndk.dir=${ANDROID_HOME}/ndk-bundle\" > local.properties'\n\n      - name: Setup gradle\n        uses: gradle/gradle-build-action@v2.5.1\n\n      - name: Run tests\n        run: ./gradlew testAllUnitTest --stacktrace\n#        run: ./gradlew testNormalDebug testFossDebug --stacktrace\n\n      - name: Publish Test Report\n        uses: mikepenz/action-junit-report@v3\n        if: success() || failure() # always run even if the previous step fails\n        with:\n          report_paths: '**/build/test-results/test*/TEST-*.xml'\n\n  android-lint:\n    name: \"🔍 Android Lint\"\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v3\n\n      - name: Set up JDK\n        uses: actions/setup-java@v3\n        with:\n          distribution: ${{ env.JAVA_DISTR }}\n          java-version: ${{ env.JAVA_VERSION }}\n\n      - name: Create google-services.json file\n        run: cat /home/runner/work/newquiz/newquiz/app/google-services.json | base64\n\n      - name: Put google-services.json data\n        env:\n          DATA: ${{ secrets.GOOGLE_SERVICES_JSON }}\n        run: echo $DATA > /home/runner/work/newquiz/newquiz/app/google-services.json\n\n      - name: Increase gradle daemon memory\n        run: \"echo \\\"org.gradle.jvmargs=-Xmx4096m\\\" >> gradle.properties\"\n\n      - name: Workaround for Android Gradle Plugin issue\n        run: 'echo \"ndk.dir=${ANDROID_HOME}/ndk-bundle\" > local.properties'\n\n      - name: Setup gradle\n        uses: gradle/gradle-build-action@v2.5.1\n\n      - name: Lint sources\n        run: ./gradlew lint --stacktrace\n\n      - name: Generate GitHub annotations\n        uses: yutailang0119/action-android-lint@v3\n        with:\n          report-path: '**/build/reports/*.xml'\n\n  assemble-apk:\n    name: \"📦 Assemble APKs\"\n    needs:\n      - test\n      - android-lint\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v3\n\n      - name: Set up JDK\n        uses: actions/setup-java@v3\n        with:\n          distribution: ${{ env.JAVA_DISTR }}\n          java-version: ${{ env.JAVA_VERSION }}\n\n      - name: Create google-services.json file\n        run: cat /home/runner/work/newquiz/newquiz/app/google-services.json | base64\n\n      - name: Put google-services.json data\n        env:\n          DATA: ${{ secrets.GOOGLE_SERVICES_JSON }}\n        run: echo $DATA > /home/runner/work/newquiz/newquiz/app/google-services.json\n\n      - name: Workaround for Android Gradle Plugin issue\n        run: 'echo \"ndk.dir=${ANDROID_HOME}/ndk-bundle\" > local.properties'\n\n      - name: Setup gradle\n        uses: gradle/gradle-build-action@v2.5.1\n\n      - name: Assemble debug APKs\n        run: ./gradlew assembleNormalDebug assembleFossDebug --stacktrace\n\n      - name: Upload APKs\n        uses: actions/upload-artifact@v3\n        with:\n          name: APKs\n          path: app/build/outputs/apk/**/app-*-universal-debug.apk\n#                app/build/outputs/apk/debug/app-universal-debug.apk\n"
  },
  {
    "path": ".gitignore",
    "content": ".gradle\n/local.properties\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\nlocal.properties\n\n/app/build\n/app/release\n\n/buildSrc/build\n\n# built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\nout/\n\n# Eclipse project files\n.classpath\n.project\n\n# IDEA/Android Studio project files, because\n# the project can be imported from settings.gradle.kts\n*.iml\n.idea/*\n!.idea/copyright\n# Keep the code styles.\n!/.idea/codeStyles\n/.idea/codeStyles/*\n!/.idea/codeStyles/Project.xml\n!/.idea/codeStyles/codeStyleConfig.xml\n!.idea/runConfigurations/\n\n.kotlin\n"
  },
  {
    "path": "LICENCE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# New Quiz\n\n[![Version](https://img.shields.io/badge/Version-1.6.2-blueviolet)](https://github.com/joaomanaia/newquiz/releases/tag/1.6.2)\n[![Android CI](https://github.com/joaomanaia/newquiz/actions/workflows/android.yml/badge.svg?branch=main)](https://github.com/joaomanaia/newquiz/actions/workflows/android.yml)\n[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0)\n[![Translation status](https://hosted.weblate.org/widgets/newquiz/-/android-strings/svg-badge.svg)](https://hosted.weblate.org/engage/newquiz)\n\n<a href='https://www.amazon.com/InfinitePower-NewQuiz/dp/B08T8JN4P9'><img height=\"75\" alt='Available at amazon appstore' src='https://images-na.ssl-images-amazon.com/images/G/01/mobile-apps/devportal2/res/images/amazon-appstore-badge-english-black.png'/></a>\n\nDo you like to challenge your knowledge? So NewQuiz is the ideal game for you.\n\n![NewQuiz purple light](pictures/NewQuiz-Promotion-purple-light.png)\n\nNew quiz is optimized to material you, the theme of new quiz will adapt to your background.\n\n![NewQuiz green night](pictures/NewQuiz-Promotion-green-night.png)\n\n# Features\n\n- Maze: Game mode with all other NewQuiz game modes in one quiz.\n\n- Multi choice quiz\n  - Logo quiz\n  - Flag quiz\n  - Solve the formula equation\n  - Number trivia\n  \n- Wordle\n  - Guess the text word\n  - Guess the number\n  - Guess the math formula\n  - Number trivia\n  \n - Comparison Quiz\n   - Compare the country population \n\n# Build With\n\n- [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) is an architectural pattern in computer software that facilitates the separation of the development of the graphical user interface\n\n- [Jetpack Compose:](https://developer.android.com/jetpack/compose) Jetpack Compose is Android’s modern toolkit for building native UI.\n- [Material 3:](https://m3.material.io/) Design and build beautiful, usable products with Material3.\n- [Kotlin:](https://kotlinlang.org/) A modern programming language that makes developers happier.\n- [Kotlin Coroutines:](https://github.com/Kotlin/kotlinx.coroutines) Asynchronous or non-blocking programming\n- [Dagger Hilt:](https://github.com/google/dagger) A fast dependency injector for Java and Android.\n- [Ktor:](https://ktor.io/) For asynchronous HTTP client requests.\n- [Lottie Android:](https://github.com/airbnb/lottie-android/) Lottie is a library that parses Adobe After Effects animations exported as json.\n- [Compose destinations:](https://github.com/raamcosta/compose-destinations) Annotation processing library for type-safe Jetpack Compose navigation with no boilerplate.\n\n# Question Data Source\n\n- [FlagCDN:](https://flagcdn.com/) country flag images\n\n- Multi choice quiz\n  - [OpenTDB:](https://opentdb.com/) multi choice questions\n  - [NumbersAPI:](http://numbersapi.com) api for number trivia questions\n- Wordle\n  - [Spanish and French words](https://github.com/lorenbrichter/Words)\n- Comparison Quiz\n  - [Rest Countries:](https://restcountries.com/) country information\n  - [TMDB:](https://www.themoviedb.org/) data about movies\n\n# Translation\n\nHello and thank you for your interest — NewQuiz is being translated using [Weblate](https://weblate.org/), a web tool designed to ease translating for both developers and translators.\n\n[![Translation status](https://hosted.weblate.org/widgets/newquiz/-/android-strings/horizontal-auto.svg)](https://hosted.weblate.org/engage/newquiz/)\n\n# Run the project locally\n\n## Requirements\n\n- [Android Studio](https://developer.android.com/studio) Hedgehog 2023.1.1 Canary 16 or later. Or use an other version of [Android Studio](https://developer.android.com/studio) but you need to change the gradle version to be compatible with your version of [Android Studio](https://developer.android.com/studio).\n  \n- Java 17 or later.\n\n## Build and Run\n\n1. First clone the repository\n\n```bash\ngit clone https://github.com/joaomanaia/newquiz.git\n```\n\n2. Open the project in [Android Studio](https://developer.android.com/studio).\n\n3. Add firebase to the project\n\n   1. Go to [Firebase](https://firebase.google.com/) and create a new project.\n   2. Add an android app to the project.\n   3. Download the google-services.json file.\n   4. Copy the file to the app folder.\n\n4. Click the run button.\n\n> **Warning**: FOSS Builds still contains proprietary code, such as Firebase, Crashlytics, and Google Play Services. In the future all proprietary code will be removed from the FOSS Builds.\n\n> **Warning**: To run the project locally you need to add firebase to the project, otherwise the you cannot build and run the project.\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\ngoogle-services.json\n\n*.apk\n*.aab\n*.dm\noutput-metadata.json"
  },
  {
    "path": "app/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.application)\n    alias(libs.plugins.newquiz.android.application.compose)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n    id(\"kotlin-parcelize\")\n    id(\"com.google.android.gms.oss-licenses-plugin\")\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz\"\n    compileSdk = 35\n\n    defaultConfig {\n        applicationId = \"com.infinitepower.newquiz\"\n        minSdk = 21\n        targetSdk = 35\n        versionCode = 16\n        versionName = \"2.0.0\"\n\n        testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n        vectorDrawables.useSupportLibrary = true\n\n        // resourceConfigurations += setOf(\"en\", \"pt\", \"fr\", \"es\", \"nb\")\n    }\n\n    androidResources {\n        generateLocaleConfig = true\n    }\n\n    buildTypes {\n        release {\n            isMinifyEnabled = true\n            isShrinkResources = true\n            isDebuggable = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android-optimize.txt\"), \"proguard-rules.pro\")\n        }\n\n        debug {\n            extra[\"enableCrashlytics\"] = false\n\n            rootProject.ext.set(\"firebasePerformanceInstrumentationEnabled\", \"false\")\n        }\n    }\n    buildFeatures {\n        compose = true\n        buildConfig = true\n    }\n\n    packaging {\n        resources {\n            excludes += \"/META-INF/{AL2.0,LGPL2.1}\"\n        }\n    }\n\n    splits {\n        abi {\n            isEnable = true\n            reset()\n            include(\"x86\", \"x86_64\", \"arm64-v8a\", \"armeabi-v7a\")\n\n            // Generate universal APK\n            isUniversalApk = true\n        }\n    }\n}\n\ndependencies {\n    implementation(libs.androidx.core.ktx)\n    implementation(libs.androidx.core.splashscreen)\n    implementation(libs.androidx.appcompat)\n\n    implementation(libs.androidx.activity.compose)\n    implementation(libs.androidx.constraintlayout.compose)\n\n    implementation(libs.androidx.lifecycle.livedata.ktx)\n    implementation(libs.androidx.lifecycle.runtimeCompose)\n    implementation(libs.androidx.lifecycle.viewModelCompose)\n\n    implementation(libs.androidx.startup.runtime)\n\n    implementation(libs.androidx.compose.ui)\n    implementation(libs.androidx.compose.ui.tooling)\n    implementation(libs.androidx.compose.material.iconsExtended)\n    implementation(libs.androidx.compose.material3)\n    implementation(libs.androidx.compose.material3.windowSizeClass)\n    debugImplementation(libs.androidx.compose.ui.testManifest)\n    androidTestImplementation(libs.androidx.compose.ui.test)\n\n    implementation(libs.google.material)\n\n    implementation(libs.hilt.navigationCompose)\n    implementation(libs.hilt.ext.work)\n    ksp(libs.hilt.ext.compiler)\n\n    testImplementation(libs.kotlinx.coroutines.test)\n    implementation(libs.kotlinx.coroutines.playServices)\n\n    implementation(libs.lottie.compose)\n\n    implementation(libs.ktor.client.core)\n    implementation(libs.ktor.client.okhttp)\n    implementation(libs.ktor.client.serialization)\n\n    implementation(libs.androidx.work.ktx)\n    androidTestImplementation(libs.androidx.work.testing)\n\n    implementation(libs.kotlinx.datetime)\n\n    implementation(libs.slf4j.simple)\n\n    implementation(libs.google.oss.licenses)\n\n    implementation(libs.kotlinx.collections.immutable)\n\n    implementation(projects.core)\n    implementation(projects.core.analytics)\n    implementation(projects.core.datastore)\n    implementation(projects.core.translation)\n    implementation(projects.core.remoteConfig)\n    implementation(projects.core.userServices)\n    implementation(projects.feature.dailyChallenge)\n    implementation(projects.feature.settings)\n    implementation(projects.feature.maze)\n    implementation(projects.feature.profile)\n    implementation(projects.model)\n    implementation(projects.multiChoiceQuiz)\n    implementation(projects.wordle)\n    implementation(projects.data)\n    implementation(projects.domain)\n    implementation(projects.comparisonQuiz)\n}\n"
  },
  {
    "path": "app/lint-baseline.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<issues format=\"6\" by=\"lint 7.4.0-alpha09\" type=\"baseline\" client=\"gradle\" dependencies=\"false\" name=\"AGP (7.4.0-alpha09)\" variant=\"fatal\" version=\"7.4.0-alpha09\">\n\n</issues>\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Keep `Companion` object fields of serializable classes.\n# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.\n-if @kotlinx.serialization.Serializable class **\n-keepclassmembers class <1> {\n    static <1>$Companion Companion;\n}\n\n# Keep `serializer()` on companion objects (both default and named) of serializable classes.\n-if @kotlinx.serialization.Serializable class ** {\n    static **$* *;\n}\n-keepclassmembers class <2>$<3> {\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n    public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n    public static <1> INSTANCE;\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.\n-keepattributes RuntimeVisibleAnnotations,AnnotationDefault\n\n-keepclassmembers @kotlinx.serialization.Serializable class ** {\n    *** Companion;\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n    public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n    public static <1> INSTANCE;\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n-keep class kotlin.reflect.** { *; }\n-dontwarn kotlin.reflect.**\n-keep class org.jetbrains.** { *; }\n\n# Firebase\n-keep class com.google.firebase.** { *; }\n-keep class com.firebase.** { *; }\n-keep class org.apache.** { *; }\n-keepnames class com.fasterxml.jackson.** { *; }\n-keepnames class javax.servlet.** { *; }\n-keepnames class org.ietf.jgss.** { *; }\n-dontwarn org.w3c.dom.**\n-dontwarn org.joda.time.**\n-dontwarn org.shaded.apache.**\n-dontwarn org.ietf.jgss.**\n-keep class com.google.android.gms.** { *; }\n-keep class org.apache** { *; }\n\n# Only necessary if you downloaded the SDK jar directly instead of from maven.\n-keep class com.shaded.fasterxml.jackson.** { *; }\n\n-dontwarn java.lang.invoke.StringConcatFactory\n\n-dontwarn kotlin.**\n-dontwarn org.w3c.dom.events.*\n-dontwarn org.jetbrains.kotlin.di.InjectorForRuntimeDescriptorLoader\n\n-keepattributes SourceFile,LineNumberTable\n\n-keep class kotlin.** { *; }\n#-keep class kotlin.reflect.** { *; }\n#-keep class org.jetbrains.kotlin.** { *; }\n\n-keepclassmembers,allowoptimization enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n    **[] $VALUES;\n    public *;\n}\n\n-keepattributes InnerClasses\n\n# Ktor\n-keep class io.ktor.** { *; }\n-keep class kotlinx.coroutines.** { *; }\n-dontwarn kotlinx.atomicfu.**\n-dontwarn io.netty.**\n-dontwarn com.typesafe.**\n-dontwarn org.slf4j.**\n\n-keepattributes *Annotation*, InnerClasses\n-dontnote kotlinx.serialization.SerializationKt\n-keep,includedescriptorclasses class com.infinitepower.newsocial.compose.**$$serializer { *; }\n\n-keep class kotlin.reflect.** { *; }\n-dontwarn kotlin.reflect.**\n-keep class org.jetbrains.** { *; }\n\n-dontwarn kotlin.**\n-dontwarn org.w3c.dom.events.*\n-dontwarn org.jetbrains.kotlin.di.InjectorForRuntimeDescriptorLoader\n\n-keepattributes SourceFile,LineNumberTable\n\n-keep class kotlin.** { *; }\n#-keep class kotlin.reflect.** { *; }\n#-keep class org.jetbrains.kotlin.** { *; }\n\n-keepclassmembers,allowoptimization enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n    **[] $VALUES;\n    public *;\n}\n\n-keepattributes InnerClasses\n\n# Ktor\n-keep class io.ktor.** { *; }\n-keep class kotlinx.coroutines.** { *; }\n-dontwarn kotlinx.atomicfu.**\n-dontwarn io.netty.**\n-dontwarn com.typesafe.**\n-dontwarn org.slf4j.**\n\n-keepattributes *Annotation*, InnerClasses\n-dontnote kotlinx.serialization.SerializationKt\n-keep,includedescriptorclasses class com.infinitepower.newsocial.compose.**$$serializer { *; }\n\n-keep class kotlin.reflect.** { *; }\n-dontwarn kotlin.reflect.**\n-keep class org.jetbrains.** { *; }\n\n-dontwarn java.lang.management.ManagementFactory\n-dontwarn java.lang.management.RuntimeMXBean"
  },
  {
    "path": "app/src/androidTest/AndroidManifest.xml",
    "content": "<manifest\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <application android:name=\".NewQuizApp\">\n        <activity\n            android:name=\"com.infinitepower.newquiz.ui.main.MainActivity\"\n            android:exported=\"true\"\n            android:hardwareAccelerated=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            tools:node=\"remove\" />\n\n        <!-- disable firebase provider to get rid of \"Default FirebaseApp is not initialized in this process\" exceptions -->\n        <provider\n            android:authorities=\"${applicationId}.firebaseperfprovider\"\n            android:name=\"com.google.firebase.perf.provider.FirebasePerfProvider\"\n            tools:node=\"remove\" />\n\n        <meta-data\n            android:name=\"firebase_performance_collection_deactivated\"\n            android:value=\"true\" />\n\n        <meta-data\n            android:name=\"firebase_crashlytics_collection_enabled\"\n            android:value=\"false\" />\n    </application>\n</manifest>"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.infinitepower.newquiz\">\n\n    <dist:module dist:instant=\"true\" />\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\n    <application\n        android:name=\".NewQuizApp\"\n        android:allowBackup=\"true\"\n        android:hardwareAccelerated=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:appCategory=\"game\"\n        android:isGame=\"true\"\n        android:theme=\"@style/Theme.NewQuiz.Starting\"\n        tools:ignore=\"UnusedAttribute\"\n        android:enableOnBackInvokedCallback=\"true\">\n        <activity\n            android:name=\"com.infinitepower.newquiz.ui.main.MainActivity\"\n            android:exported=\"true\"\n            android:hardwareAccelerated=\"true\"\n            android:theme=\"@style/Theme.NewQuiz.Starting\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <meta-data\n                android:name=\"android.app.shortcuts\"\n                android:resource=\"@xml/shortcuts\" />\n        </activity>\n\n        <activity\n            android:name=\"com.google.android.gms.oss.licenses.OssLicensesMenuActivity\"\n            android:theme=\"@style/Theme.NewQuiz\" />\n\n        <activity\n            android:name=\"com.google.android.gms.oss.licenses.OssLicensesActivity\"\n            android:theme=\"@style/Theme.NewQuiz\" />\n\n        <meta-data\n            android:name=\"firebase_performance_logcat_enabled\"\n            android:value=\"true\" />\n\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            android:exported=\"false\"\n            tools:node=\"merge\">\n            <meta-data\n                android:name=\"androidx.work.WorkManagerInitializer\"\n                android:value=\"androidx.startup\"\n                tools:node=\"remove\" />\n\n            <!-- WorkManager Initializer-->\n            <meta-data\n                android:name=\"com.infinitepower.newquiz.initializer.WorkManagerInitializer\"\n                android:value=\"androidx.startup\" />\n\n            <meta-data\n                android:name=\"com.infinitepower.newquiz.initializer.EnqueueStartWorksInitializer\"\n                android:value=\"androidx.startup\" />\n        </provider>\n\n        <!-- Turn off firebase analytics automatically data collection -->\n        <meta-data android:name=\"firebase_analytics_collection_enabled\" android:value=\"false\" />\n\n        <!-- Turn off firebase crashlytics automatically data collection -->\n        <meta-data android:name=\"firebase_crashlytics_collection_enabled\" android:value=\"false\" />\n\n        <!-- Turn off firebase performance automatically data collection -->\n        <meta-data android:name=\"firebase_performance_collection_enabled\" android:value=\"false\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/NewQuizApp.kt",
    "content": "package com.infinitepower.newquiz\n\nimport android.app.Application\nimport dagger.hilt.android.HiltAndroidApp\n\n@HiltAndroidApp\nclass NewQuizApp : Application()\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/core/navigation/AppNavGraphs.kt",
    "content": "package com.infinitepower.newquiz.core.navigation\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.windowsizeclass.WindowSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.Modifier\nimport androidx.navigation.NavHostController\nimport com.infinitepower.newquiz.comparison_quiz.destinations.ComparisonQuizListScreenDestination\nimport com.infinitepower.newquiz.comparison_quiz.destinations.ComparisonQuizScreenDestination\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.feature.daily_challenge.destinations.DailyChallengeScreenDestination\nimport com.infinitepower.newquiz.feature.maze.destinations.LevelResultsScreenDestination\nimport com.infinitepower.newquiz.feature.maze.destinations.MazeCategoriesInfoScreenDestination\nimport com.infinitepower.newquiz.feature.maze.destinations.MazeScreenDestination\nimport com.infinitepower.newquiz.feature.profile.destinations.ProfileScreenDestination\nimport com.infinitepower.newquiz.feature.settings.destinations.SettingsScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizListScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizResultsScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.SavedMultiChoiceQuestionsScreenDestination\nimport com.infinitepower.newquiz.ui.navigation.NavigationContainer\nimport com.infinitepower.newquiz.wordle.destinations.WordleListScreenDestination\nimport com.infinitepower.newquiz.wordle.destinations.WordleScreenDestination\nimport com.ramcosta.composedestinations.DestinationsNavHost\nimport com.ramcosta.composedestinations.animations.defaults.RootNavGraphDefaultAnimations\nimport com.ramcosta.composedestinations.navigation.dependency\nimport com.ramcosta.composedestinations.rememberNavHostEngine\nimport com.ramcosta.composedestinations.scope.DestinationScopeWithNoDependencies\nimport com.ramcosta.composedestinations.spec.DestinationSpec\nimport com.ramcosta.composedestinations.spec.NavGraphSpec\nimport com.ramcosta.composedestinations.spec.Route\n\n@Immutable\ninternal data class NavGraph(\n    override val route: String,\n    override val startRoute: Route,\n    val destinations: List<DestinationSpec<out Any>>,\n    override val nestedNavGraphs: List<NavGraph> = emptyList()\n) : NavGraphSpec {\n    override val destinationsByRoute: Map<String, DestinationSpec<out Any>> =\n        destinations.associateBy { it.route }\n}\n\ninternal object AppNavGraphs {\n    val mainNavGraph = NavGraph(\n        route = \"main_nav_graph\",\n        startRoute = MultiChoiceQuizListScreenDestination,\n        destinations = listOf(\n            MultiChoiceQuizScreenDestination,\n            MultiChoiceQuizListScreenDestination,\n            MultiChoiceQuizResultsScreenDestination,\n            SettingsScreenDestination,\n            SavedMultiChoiceQuestionsScreenDestination,\n            WordleScreenDestination,\n            WordleListScreenDestination,\n            MazeScreenDestination,\n            MazeCategoriesInfoScreenDestination,\n            LevelResultsScreenDestination,\n            ComparisonQuizScreenDestination,\n            ComparisonQuizListScreenDestination,\n            DailyChallengeScreenDestination,\n            ProfileScreenDestination\n        )\n    )\n}\n\ninternal fun DestinationScopeWithNoDependencies<*>.currentNavigator(\n    remoteConfig: RemoteConfig\n): CommonNavGraphNavigator {\n    return CommonNavGraphNavigator(destinationsNavigator, navController, remoteConfig)\n}\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun AppNavigation(\n    modifier: Modifier = Modifier,\n    navController: NavHostController,\n    windowSizeClass: WindowSizeClass,\n    remoteConfig: RemoteConfig,\n    dailyChallengeClaimCount: Int,\n    userDiamonds: UInt\n) {\n    NavigationContainer(\n        navController = navController,\n        windowWidthSize = windowSizeClass.widthSizeClass,\n        dailyChallengeClaimCount = dailyChallengeClaimCount,\n        userDiamonds = userDiamonds\n    ) { innerPadding ->\n        DestinationsNavHost(\n            navGraph = AppNavGraphs.mainNavGraph,\n            navController = navController,\n            modifier = modifier.padding(innerPadding),\n            dependenciesContainerBuilder = {\n                dependency(currentNavigator(remoteConfig))\n                dependency(windowSizeClass)\n            },\n            engine = rememberNavHostEngine(\n                rootDefaultAnimations = RootNavGraphDefaultAnimations.ACCOMPANIST_FADING,\n            )\n        )\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/core/navigation/CommonNavGraphNavigator.kt",
    "content": "package com.infinitepower.newquiz.core.navigation\n\nimport androidx.navigation.NavController\nimport com.infinitepower.newquiz.comparison_quiz.destinations.ComparisonQuizScreenDestination\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.data.util.mappers.comparisonquiz.toEntity\nimport com.infinitepower.newquiz.feature.daily_challenge.DailyChallengeScreenNavigator\nimport com.infinitepower.newquiz.feature.maze.destinations.LevelResultsScreenDestination\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.saved_questions.SavedQuestionsScreenNavigator\nimport com.infinitepower.newquiz.wordle.destinations.WordleScreenDestination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport com.ramcosta.composedestinations.utils.destination\n\nclass CommonNavGraphNavigator(\n    private val navigator: DestinationsNavigator,\n    private val navController: NavController,\n    private val remoteConfig: RemoteConfig\n) : SavedQuestionsScreenNavigator,\n    MazeNavigator,\n    DailyChallengeScreenNavigator {\n\n    override fun navigateToWordleQuiz(type: WordleQuizType) {\n        navigator.navigate(WordleScreenDestination(quizType = type))\n    }\n\n    override fun navigateToComparisonQuiz(\n        category: ComparisonQuizCategory,\n        mode: ComparisonMode\n    ) {\n        navigator.navigate(ComparisonQuizScreenDestination(category.id, mode))\n    }\n\n    override fun navigateToMultiChoiceQuiz(initialQuestions: ArrayList<MultiChoiceQuestion>) {\n        val remoteConfigDifficulty = remoteConfig\n            .get(RemoteConfigValue.MULTICHOICE_QUICKQUIZ_DIFFICULTY)\n            .run {\n                if (this == \"random\") null else this\n            }\n\n        navigator.navigate(\n            MultiChoiceQuizScreenDestination(\n                initialQuestions = initialQuestions,\n                difficulty = remoteConfigDifficulty\n            )\n        )\n    }\n\n    override fun navigateToMultiChoiceQuiz(category: MultiChoiceBaseCategory) {\n        val remoteConfigDifficulty = remoteConfig\n            .get(RemoteConfigValue.MULTICHOICE_QUICKQUIZ_DIFFICULTY)\n            .run {\n                if (this == \"random\") null else this\n            }\n\n        navigator.navigate(\n            MultiChoiceQuizScreenDestination(\n                difficulty = remoteConfigDifficulty,\n                category = category\n            )\n        )\n    }\n\n    override fun navigateToGame(item: MazeQuiz.MazeItem) {\n        val destination = when (item) {\n            is MazeQuiz.MazeItem.Wordle -> {\n                WordleScreenDestination(\n                    word = item.wordleWord.word,\n                    quizType = item.wordleQuizType,\n                    mazeItemId = item.id.toString(),\n                    textHelper = item.wordleWord.textHelper\n                )\n            }\n\n            is MazeQuiz.MazeItem.MultiChoice -> {\n                MultiChoiceQuizScreenDestination(\n                    initialQuestions = arrayListOf(item.question),\n                    mazeItemId = item.id.toString()\n                )\n            }\n\n            is MazeQuiz.MazeItem.ComparisonQuiz -> {\n                val initialItems = item.question.questions\n                    .toList()\n                    .map(ComparisonQuizItem::toEntity)\n\n                ComparisonQuizScreenDestination(\n                    categoryId = item.question.categoryId,\n                    initialItems = ArrayList(initialItems),\n                    mazeItemId = item.id\n                )\n            }\n        }\n\n        navigator.navigate(destination) {\n            popUpTo(LevelResultsScreenDestination) {\n                inclusive = true\n            }\n        }\n    }\n\n    override fun navigateToMazeResults(itemId: Int) {\n        navigator.navigate(LevelResultsScreenDestination(itemId)) {\n            launchSingleTop = true\n\n            // Remove current screen from back stack\n            val currentDestination = navController.currentBackStackEntry?.destination()\n            if (currentDestination != null) {\n                popUpTo(currentDestination) {\n                    inclusive = true\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/core/navigation/NavigationItem.kt",
    "content": "package com.infinitepower.newquiz.core.navigation\n\nimport androidx.annotation.Keep\nimport androidx.annotation.StringRes\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport com.ramcosta.composedestinations.spec.Direction\n\nsealed class NavigationItem {\n    @get:StringRes\n    abstract val text: Int\n\n    abstract val group: NavDrawerItemGroup?\n\n    @Immutable\n    data class Label(\n        @StringRes override val text: Int,\n        override val group: NavDrawerItemGroup? = null\n    ) : NavigationItem()\n\n    /**\n     * @param primary items to navigation bar\n     */\n    @Immutable\n    data class Item(\n        @StringRes override val text: Int,\n        override val group: NavDrawerItemGroup? = null,\n        val selectedIcon: ImageVector,\n        val unselectedIcon: ImageVector? = null,\n        val badge: NavDrawerBadgeItem? = null,\n        val direction: Direction,\n        val primary: Boolean = false,\n        val screenType: ScreenType = ScreenType.NORMAL\n    ) : NavigationItem() {\n        fun getIcon(selected: Boolean): ImageVector {\n            return if (selected || unselectedIcon == null) selectedIcon else unselectedIcon\n        }\n    }\n}\n\nenum class ScreenType {\n    /** When using this type navigation items will be visible and have a top bar */\n    NORMAL,\n\n    /** When using this type all navigation items will be invisible and have no top bar */\n    NAVIGATION_HIDDEN\n}\n\n@JvmInline\nvalue class NavDrawerItemGroup(val key: String)\n\n@Keep\ndata class NavDrawerBadgeItem(\n    val value: Int,\n    val description: String\n)\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/core/workers/AppStartLoggingAnalyticsWorker.kt",
    "content": "package com.infinitepower.newquiz.core.workers\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.OneTimeWorkRequestBuilder\nimport androidx.work.WorkManager\nimport androidx.work.WorkerParameters\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.analytics.UserProperty\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.translation.TranslatorUtil\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\n\n@HiltWorker\nclass AppStartLoggingAnalyticsWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager,\n    private val translatorUtil: TranslatorUtil,\n    private val analyticsHelper: AnalyticsHelper\n) : CoroutineWorker(appContext, workerParams) {\n    companion object {\n        fun enqueue(workManager: WorkManager) {\n            val appStartLoggingAnalyticsWorker = OneTimeWorkRequestBuilder<AppStartLoggingAnalyticsWorker>().build()\n            workManager.enqueue(appStartLoggingAnalyticsWorker)\n        }\n    }\n\n    override suspend fun doWork(): Result {\n        val wordleLang = settingsDataStoreManager.getPreference(SettingsCommon.InfiniteWordleQuizLanguage)\n        analyticsHelper.setUserProperty(UserProperty.WordleLanguage(wordleLang))\n\n        val isTranslatorModelDownloaded = translatorUtil.isModelDownloaded()\n        analyticsHelper.setUserProperty(UserProperty.TranslatorModelDownloaded(isTranslatorModelDownloaded))\n\n        return Result.success()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/initializer/EnqueueStartWorksInitializer.kt",
    "content": "package com.infinitepower.newquiz.initializer\n\nimport android.content.Context\nimport androidx.startup.Initializer\nimport androidx.work.WorkManager\nimport com.infinitepower.newquiz.core.remote_config.initializer.RemoteConfigInitializer\nimport com.infinitepower.newquiz.core.workers.AppStartLoggingAnalyticsWorker\nimport com.infinitepower.newquiz.data.worker.daily_challenge.VerifyDailyChallengeWorker\n\n@Suppress(\"unused\")\nclass EnqueueStartWorksInitializer : Initializer<Unit> {\n    override fun create(context: Context) {\n        val workManager = WorkManager.getInstance(context)\n\n        VerifyDailyChallengeWorker.enqueueUniquePeriodicWork(workManager)\n        AppStartLoggingAnalyticsWorker.enqueue(workManager)\n    }\n\n    override fun dependencies(): List<Class<out Initializer<*>>> = listOf(\n        WorkManagerInitializer::class.java,\n        RemoteConfigInitializer::class.java\n    )\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/initializer/WorkManagerInitializer.kt",
    "content": "package com.infinitepower.newquiz.initializer\n\nimport android.content.Context\nimport android.util.Log\nimport androidx.hilt.work.HiltWorkerFactory\nimport androidx.startup.Initializer\nimport androidx.work.Configuration\nimport androidx.work.WorkManager\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.EntryPoint\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.EntryPointAccessors\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nclass WorkManagerInitializer : Initializer<WorkManager> {\n    @Provides\n    @Singleton\n    override fun create(@ApplicationContext context: Context): WorkManager {\n        if (!WorkManager.isInitialized()) {\n            val entryPoint = EntryPointAccessors.fromApplication<WorkManagerInitializerEntryPoint>(context)\n\n            val configuration = Configuration\n                .Builder()\n                .setWorkerFactory(entryPoint.hiltWorkerFactory())\n                .setMinimumLoggingLevel(Log.INFO)\n                .build()\n\n            WorkManager.initialize(context, configuration)\n        }\n\n        return WorkManager.getInstance(context)\n    }\n\n    override fun dependencies(): List<Class<out Initializer<*>>> {\n        return emptyList()\n    }\n\n    @EntryPoint\n    @InstallIn(SingletonComponent::class)\n    interface WorkManagerInitializerEntryPoint {\n        fun hiltWorkerFactory(): HiltWorkerFactory\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/components/DataCollectionConsentDialog.kt",
    "content": "package com.infinitepower.newquiz.ui.components\n\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.rememberScrollState\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.foundation.verticalScroll\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Shape\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.window.DialogProperties\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun DataCollectionConsentDialog(\n    onAgreeClick: () -> Unit = {},\n    onDisagreeClick: () -> Unit = {}\n) {\n    AlertDialog(\n        title = { Text(text = stringResource(id = CoreR.string.data_collection_consent)) },\n        text = {\n            DialogConsentContent(\n                onAgreeClick = onAgreeClick,\n                onDisagreeClick = onDisagreeClick\n            )\n        },\n        onDismissRequest = {},\n        confirmButton = {},\n        properties = DialogProperties(\n            dismissOnBackPress = false,\n            dismissOnClickOutside = false\n        )\n    )\n}\n\n@Composable\nprivate fun DialogConsentContent(\n    modifier: Modifier = Modifier,\n    onAgreeClick: () -> Unit = {},\n    onDisagreeClick: () -> Unit = {}\n) {\n    val spaceLarge = MaterialTheme.spacing.large\n\n    val scrollState = rememberScrollState()\n\n    Column(\n        modifier = modifier.height(400.dp)\n    ) {\n        Text(\n            text = stringResource(id = CoreR.string.data_collection_consent_description),\n            style = MaterialTheme.typography.bodyLarge,\n            modifier = Modifier\n                .weight(1f)\n                .verticalScroll(scrollState)\n        )\n        Spacer(modifier = Modifier.height(spaceLarge))\n        ConsentButtons(\n            onAgreeClick = onAgreeClick,\n            onDisagreeClick = onDisagreeClick\n        )\n    }\n}\n\n@Composable\nprivate fun ConsentButtons(\n    modifier: Modifier = Modifier,\n    onAgreeClick: () -> Unit = {},\n    onDisagreeClick: () -> Unit = {}\n) {\n    Column(\n        modifier = modifier.fillMaxWidth()\n    ) {\n        ConsentButton(\n            text = stringResource(id = CoreR.string.data_collection_consent_agree),\n            onClick = onAgreeClick,\n            shape = RoundedCornerShape(\n                topStart = 16.dp,\n                topEnd = 16.dp,\n                bottomStart = 8.dp,\n                bottomEnd = 8.dp\n            )\n        )\n        Spacer(modifier = Modifier.height(MaterialTheme.spacing.small))\n        ConsentButton(\n            text = stringResource(id = CoreR.string.data_collection_consent_disagree),\n            onClick = onDisagreeClick,\n            shape = RoundedCornerShape(\n                topStart = 8.dp,\n                topEnd = 8.dp,\n                bottomStart = 16.dp,\n                bottomEnd = 16.dp\n            )\n        )\n    }\n}\n\n@Composable\nprivate fun ConsentButton(\n    text: String,\n    shape: Shape = MaterialTheme.shapes.medium,\n    onClick: () -> Unit\n) {\n    val buttonColor = if (isSystemInDarkTheme()) {\n        MaterialTheme.colorScheme.primary\n    } else {\n        MaterialTheme.colorScheme.primaryContainer\n    }\n\n    Surface(\n        onClick = onClick,\n        color = buttonColor,\n        shape = shape,\n        modifier = Modifier.fillMaxWidth()\n    ) {\n        Row(\n            modifier = Modifier.fillMaxWidth(),\n            horizontalArrangement = Arrangement.Center\n        ) {\n            Text(\n                modifier = Modifier.padding(20.dp),\n                text = text,\n                style = MaterialTheme.typography.titleMedium\n            )\n        }\n    }\n}\n\n@OptIn(ExperimentalMaterial3Api::class)\n@Composable\n@PreviewLightDark\nprivate fun AnalyticsCollectionConsentContentPreview() {\n    NewQuizTheme {\n        Surface {\n            DataCollectionConsentDialog()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/components/DiamondsCounter.kt",
    "content": "package com.infinitepower.newquiz.ui.components\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\n\n@Composable\ninternal fun DiamondsCounter(\n    modifier: Modifier = Modifier,\n    diamonds: UInt\n) {\n    Surface(\n        modifier = modifier,\n        shape = MaterialTheme.shapes.small,\n        color = MaterialTheme.colorScheme.surface,\n        tonalElevation = 4.dp\n    ) {\n        Text(\n            text = \"$diamonds \\uD83D\\uDC8E\",\n            modifier = Modifier.padding(\n                horizontal = MaterialTheme.spacing.small,\n                vertical = MaterialTheme.spacing.extraSmall\n            ),\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun DiamondsCounterPreview() {\n    NewQuizTheme {\n        Surface {\n            DiamondsCounter(\n                modifier = Modifier.padding(16.dp),\n                diamonds = 100u\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/main/MainActivity.kt",
    "content": "package com.infinitepower.newquiz.ui.main\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.activity.enableEdgeToEdge\nimport androidx.activity.viewModels\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.calculateWindowSizeClass\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.lifecycleScope\nimport androidx.lifecycle.repeatOnLifecycle\nimport androidx.navigation.compose.rememberNavController\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.analytics.LocalAnalyticsHelper\nimport com.infinitepower.newquiz.core.navigation.AppNavigation\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.ui.components.DataCollectionConsentDialog\nimport dagger.hilt.android.AndroidEntryPoint\nimport kotlinx.coroutines.flow.collect\nimport kotlinx.coroutines.flow.onEach\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@AndroidEntryPoint\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class, ExperimentalMaterial3Api::class)\nclass MainActivity : ComponentActivity() {\n    @Inject\n    lateinit var analyticsHelper: AnalyticsHelper\n\n    @Inject\n    lateinit var remoteConfig: RemoteConfig\n\n    private val viewModel by viewModels<MainViewModel>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        val splashScreen = installSplashScreen()\n        super.onCreate(savedInstanceState)\n\n        var uiState: MainScreenUiState by mutableStateOf(MainScreenUiState())\n\n        // Update the uiState\n        lifecycleScope.launch {\n            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {\n                viewModel.uiState\n                    .onEach { uiState = it }\n                    .collect()\n            }\n        }\n\n        // Keep the splash screen until the uiState is loaded\n        splashScreen.setKeepOnScreenCondition { uiState.loading }\n\n        enableEdgeToEdge()\n\n        setContent {\n            CompositionLocalProvider(\n                LocalAnalyticsHelper provides analyticsHelper\n            ) {\n                NewQuizTheme(\n                    animationsEnabled = uiState.animationsEnabled,\n                ) {\n                    val windowSize = calculateWindowSizeClass(activity = this)\n\n                    Surface(\n                        color = MaterialTheme.colorScheme.background\n                    ) {\n                        AppNavigation(\n                            navController = rememberNavController(),\n                            modifier = Modifier.fillMaxSize(),\n                            windowSizeClass = windowSize,\n                            remoteConfig = remoteConfig,\n                            dailyChallengeClaimCount = uiState.dailyChallengeClaimableCount,\n                            userDiamonds = uiState.userDiamonds,\n                        )\n\n                        if (uiState.showDataAnalyticsConsentDialog && !uiState.loading) {\n                            DataCollectionConsentDialog(\n                                onAgreeClick = {\n                                    viewModel.onEvent(\n                                        MainScreenUiEvent.OnDataAnalyticsConsentClick(\n                                            agreed = true\n                                        )\n                                    )\n                                },\n                                onDisagreeClick = {\n                                    viewModel.onEvent(\n                                        MainScreenUiEvent.OnDataAnalyticsConsentClick(\n                                            agreed = false\n                                        )\n                                    )\n                                }\n                            )\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/main/MainScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.ui.main\n\nimport androidx.annotation.Keep\n\nsealed interface MainScreenUiEvent {\n    @Keep\n    data class OnDataAnalyticsConsentClick(\n        val agreed: Boolean\n    ) : MainScreenUiEvent\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/main/MainScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.ui.main\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.core.theme.AnimationsEnabled\n\n/**\n * Represents the state of the main screen.\n *\n * @param loading True if the main screen state is loading. The splash screen will be shown until this is false.\n */\n@Keep\ndata class MainScreenUiState(\n    val loading: Boolean = true,\n    val showDataAnalyticsConsentDialog: Boolean = false,\n    val dailyChallengeClaimableCount: Int = 0,\n    val animationsEnabled: AnimationsEnabled = AnimationsEnabled(),\n    val userDiamonds: UInt = 0u\n)\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/main/MainViewModel.kt",
    "content": "package com.infinitepower.newquiz.ui.main\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.theme.AnimationsEnabled\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.StateFlow\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.stateIn\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\nclass MainViewModel @Inject constructor(\n    dailyChallengeRepository: DailyChallengeRepository,\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager,\n    private val analyticsHelper: AnalyticsHelper,\n    private val userService: UserService\n) : ViewModel() {\n    private val animationsEnabledFlow = combine(\n        settingsDataStoreManager.getPreferenceFlow(SettingsCommon.GlobalAnimationsEnabled),\n        settingsDataStoreManager.getPreferenceFlow(SettingsCommon.WordleAnimationsEnabled),\n        settingsDataStoreManager.getPreferenceFlow(SettingsCommon.MultiChoiceAnimationsEnabled),\n    ) { globalAnimationsEnabled, wordleAnimationsEnabled, multiChoiceAnimationsEnabled ->\n        AnimationsEnabled(\n            global = globalAnimationsEnabled,\n            wordle = wordleAnimationsEnabled && globalAnimationsEnabled,\n            multiChoice = multiChoiceAnimationsEnabled && globalAnimationsEnabled\n        )\n    }\n\n    val uiState: StateFlow<MainScreenUiState> = combine(\n        animationsEnabledFlow,\n        analyticsHelper.showDataAnalyticsConsentDialog,\n        dailyChallengeRepository.getClaimableTasksCountFlow(),\n        userService.getUserDiamondsFlow()\n    ) { animationsEnabled, showDataAnalyticsConsentDialog, dailyChallengeClaimableCount, userDiamonds ->\n        MainScreenUiState(\n            loading = false,\n            animationsEnabled = animationsEnabled,\n            showDataAnalyticsConsentDialog = showDataAnalyticsConsentDialog,\n            dailyChallengeClaimableCount = dailyChallengeClaimableCount,\n            userDiamonds = userDiamonds\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        initialValue = MainScreenUiState(),\n        started = SharingStarted.WhileSubscribed(UI_STATE_STOP_TIMEOUT)\n    )\n\n    companion object {\n        private const val UI_STATE_STOP_TIMEOUT = 5_000L\n    }\n\n    fun onEvent(event: MainScreenUiEvent) {\n        when (event) {\n            is MainScreenUiEvent.OnDataAnalyticsConsentClick -> viewModelScope.launch(Dispatchers.IO) {\n                analyticsHelper.updateDataConsent(event.agreed)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/navigation/CompactNavigationContainer.kt",
    "content": "package com.infinitepower.newquiz.ui.navigation\n\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxHeight\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Menu\nimport androidx.compose.material3.CenterAlignedTopAppBar\nimport androidx.compose.material3.DrawerState\nimport androidx.compose.material3.DrawerValue\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.ModalNavigationDrawer\nimport androidx.compose.material3.NavigationBar\nimport androidx.compose.material3.NavigationBarItem\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.SnackbarHost\nimport androidx.compose.material3.SnackbarHostState\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBarDefaults\nimport androidx.compose.material3.rememberDrawerState\nimport androidx.compose.material3.rememberTopAppBarState\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.navigation.NavController\nimport androidx.navigation.compose.rememberNavController\nimport com.infinitepower.newquiz.core.navigation.NavigationItem\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizScreenDestination\nimport com.infinitepower.newquiz.ui.components.DiamondsCounter\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.coroutines.launch\n\n/**\n * Container with navigation bar and modal drawer\n */\n@Composable\n@ExperimentalMaterial3Api\ninternal fun CompactContainer(\n    navigator: DestinationsNavigator,\n    navController: NavController,\n    primaryItems: ImmutableList<NavigationItem.Item>,\n    otherItems: ImmutableList<NavigationItem>,\n    selectedItem: NavigationItem.Item?,\n    userDiamonds: UInt = 0u,\n    drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),\n    snackbarHostState: SnackbarHostState,\n    content: @Composable (PaddingValues) -> Unit\n) {\n    val scope = rememberCoroutineScope()\n\n    val text = selectedItem?.text?.let { id ->\n        stringResource(id = id)\n    } ?: \"NewQuiz\"\n\n    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(\n        rememberTopAppBarState()\n    )\n\n    ModalNavigationDrawer(\n        drawerState = drawerState,\n        drawerContent = {\n            NavigationDrawerContent(\n                modifier = Modifier.fillMaxHeight(),\n                permanent = false,\n                selectedItem = selectedItem,\n                items = otherItems,\n                onItemClick = { item ->\n                    scope.launch { drawerState.close() }\n                    navigator.navigate(item.direction)\n                },\n            )\n        }\n    ) {\n        Scaffold(\n            modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),\n            snackbarHost = {\n                SnackbarHost(hostState = snackbarHostState)\n            },\n            topBar = {\n                CenterAlignedTopAppBar(\n                    title = {\n                        Text(text = text)\n                    },\n                    scrollBehavior = scrollBehavior,\n                    navigationIcon = {\n                        IconButton(\n                            onClick = {\n                                scope.launch { drawerState.open() }\n                            }\n                        ) {\n                            Icon(\n                                imageVector = Icons.Rounded.Menu,\n                                contentDescription = \"Open menu\"\n                            )\n                        }\n                    },\n                    actions = {\n                        DiamondsCounter(\n                            diamonds = userDiamonds,\n                            modifier = Modifier\n                        )\n                    }\n                )\n            },\n            bottomBar = {\n                CompactBottomBar(\n                    selectedItem = selectedItem,\n                    primaryItems = primaryItems,\n                    navController = navController\n                )\n            },\n            content = content\n        )\n    }\n}\n\n@Composable\nprivate fun CompactBottomBar(\n    modifier: Modifier = Modifier,\n    selectedItem: NavigationItem.Item?,\n    primaryItems: ImmutableList<NavigationItem.Item>,\n    navController: NavController\n) {\n    NavigationBar(\n        modifier = modifier,\n    ) {\n        primaryItems.forEach { item ->\n            NavigationBarItem(\n                selected = item == selectedItem,\n                onClick = {\n                    navController.navigate(item.direction.route) {\n                        // Pop up to the start destination of the graph to\n                        // avoid building up a large stack of destinations\n                        // on the back stack as users select items\n                        val startDestination = navController.graph.startDestinationRoute\n                        popUpTo(startDestination ?: MultiChoiceQuizScreenDestination.route)\n                        // Avoid multiple copies of the same destination when re-selecting the same item\n                        launchSingleTop = true\n                    }\n                },\n                icon = {\n                    Icon(\n                        imageVector = item.getIcon(item == selectedItem),\n                        contentDescription = stringResource(id = item.text)\n                    )\n                }\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun CompactContainerPreview() {\n    val otherItems = getOtherItems()\n\n    val selectedItem = otherItems\n        .filterIsInstance<NavigationItem.Item>()\n        .firstOrNull()\n\n    NewQuizTheme {\n        Surface {\n            CompactContainer(\n                navController = rememberNavController(),\n                navigator = EmptyDestinationsNavigator,\n                content = {\n                    Text(text = \"NewQuiz\")\n                },\n                primaryItems = getPrimaryItems(),\n                otherItems = otherItems,\n                selectedItem = selectedItem,\n                userDiamonds = 100u,\n                snackbarHostState = SnackbarHostState()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/navigation/ExpandedNavigationContainer.kt",
    "content": "package com.infinitepower.newquiz.ui.navigation\n\nimport android.content.res.Configuration\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxHeight\nimport androidx.compose.material3.CenterAlignedTopAppBar\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.PermanentNavigationDrawer\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.SnackbarHost\nimport androidx.compose.material3.SnackbarHostState\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBarDefaults\nimport androidx.compose.material3.rememberTopAppBarState\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.Preview\nimport com.infinitepower.newquiz.core.navigation.NavigationItem\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.ui.components.DiamondsCounter\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.toImmutableList\n\n/**\n * Container with permanent navigation drawer\n */\n@Composable\n@ExperimentalMaterial3Api\ninternal fun ExpandedContainer(\n    navigator: DestinationsNavigator,\n    primaryItems: ImmutableList<NavigationItem.Item>,\n    otherItems: ImmutableList<NavigationItem>,\n    selectedItem: NavigationItem.Item?,\n    userDiamonds: UInt = 0u,\n    snackbarHostState: SnackbarHostState,\n    content: @Composable (PaddingValues) -> Unit\n) {\n    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(\n        rememberTopAppBarState()\n    )\n\n    val text = selectedItem?.text?.let { id ->\n        stringResource(id = id)\n    } ?: \"NewQuiz\"\n\n    val allNavigationItems = remember(primaryItems, otherItems) {\n        (primaryItems + otherItems).toImmutableList()\n    }\n\n    PermanentNavigationDrawer(\n        drawerContent = {\n            NavigationDrawerContent(\n                modifier = Modifier.fillMaxHeight(),\n                permanent = true,\n                selectedItem = selectedItem,\n                items = allNavigationItems,\n                onItemClick = { item ->\n                    navigator.navigate(item.direction)\n                },\n            )\n        }\n    ) {\n        Scaffold(\n            modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),\n            snackbarHost = {\n                SnackbarHost(hostState = snackbarHostState)\n            },\n            topBar = {\n                CenterAlignedTopAppBar(\n                    title = {\n                        Text(text = text)\n                    },\n                    scrollBehavior = scrollBehavior,\n                    actions = {\n                        DiamondsCounter(\n                            diamonds = userDiamonds,\n                            modifier = Modifier\n                        )\n                    }\n                )\n            },\n            content = content\n        )\n    }\n}\n\n@Composable\n@Preview(\n    showBackground = true,\n    device = \"spec:width=1280dp,height=800dp,dpi=480\",\n    group = \"Expanded\"\n)\n@Preview(\n    showBackground = true,\n    uiMode = Configuration.UI_MODE_NIGHT_YES,\n    device = \"spec:width=1280dp,height=800dp,dpi=480\",\n    group = \"Expanded\"\n)\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun MediumContainerPreview() {\n    val otherItems = getOtherItems()\n\n    val selectedItem = otherItems\n        .filterIsInstance<NavigationItem.Item>()\n        .firstOrNull()\n\n    NewQuizTheme {\n        Surface {\n            ExpandedContainer(\n                navigator = EmptyDestinationsNavigator,\n                content = {\n                    Text(text = \"NewQuiz\")\n                },\n                primaryItems = getPrimaryItems(),\n                otherItems = otherItems,\n                selectedItem = selectedItem,\n                userDiamonds = 100u,\n                snackbarHostState = SnackbarHostState()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/navigation/MediumNavigationContainer.kt",
    "content": "package com.infinitepower.newquiz.ui.navigation\n\nimport android.content.res.Configuration\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxHeight\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Menu\nimport androidx.compose.material3.CenterAlignedTopAppBar\nimport androidx.compose.material3.DrawerState\nimport androidx.compose.material3.DrawerValue\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.ModalNavigationDrawer\nimport androidx.compose.material3.NavigationRail\nimport androidx.compose.material3.NavigationRailItem\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.SnackbarHost\nimport androidx.compose.material3.SnackbarHostState\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBarDefaults\nimport androidx.compose.material3.rememberDrawerState\nimport androidx.compose.material3.rememberTopAppBarState\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.Preview\nimport com.infinitepower.newquiz.core.navigation.NavigationItem\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.ui.components.DiamondsCounter\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.coroutines.launch\n\n/**\n * Container with navigation rail and modal drawer\n */\n@Composable\n@ExperimentalMaterial3Api\ninternal fun MediumContainer(\n    navigator: DestinationsNavigator,\n    primaryItems: ImmutableList<NavigationItem.Item>,\n    otherItems: ImmutableList<NavigationItem>,\n    selectedItem: NavigationItem.Item?,\n    userDiamonds: UInt = 0u,\n    drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),\n    snackbarHostState: SnackbarHostState,\n    content: @Composable (PaddingValues) -> Unit\n) {\n    val scope = rememberCoroutineScope()\n\n    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(\n        rememberTopAppBarState()\n    )\n\n    val text = selectedItem?.text?.let { id ->\n        stringResource(id = id)\n    } ?: \"NewQuiz\"\n\n    ModalNavigationDrawer(\n        drawerState = drawerState,\n        drawerContent = {\n            NavigationDrawerContent(\n                modifier = Modifier.fillMaxHeight(),\n                permanent = false,\n                selectedItem = selectedItem,\n                items = otherItems,\n                onItemClick = { item ->\n                    scope.launch { drawerState.close() }\n                    navigator.navigate(item.direction)\n                }\n            )\n        }\n    ) {\n        Row {\n            NavigationRail(\n                header = {\n                    IconButton(\n                        onClick = {\n                            scope.launch { drawerState.open() }\n                        }\n                    ) {\n                        Icon(\n                            imageVector = Icons.Rounded.Menu,\n                            contentDescription = \"Open menu\"\n                        )\n                    }\n                }\n            ) {\n                primaryItems.forEach { item ->\n                    NavigationRailItem(\n                        selected = item == selectedItem,\n                        onClick = { navigator.navigate(item.direction) },\n                        icon = {\n                            Icon(\n                                imageVector = item.getIcon(item == selectedItem),\n                                contentDescription = stringResource(id = item.text)\n                            )\n                        }\n                    )\n                }\n            }\n\n            Scaffold(\n                modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),\n                snackbarHost = {\n                    SnackbarHost(hostState = snackbarHostState)\n                },\n                topBar = {\n                    CenterAlignedTopAppBar(\n                        scrollBehavior = scrollBehavior,\n                        title = {\n                            Text(text = text)\n                        },\n                        actions = {\n                            DiamondsCounter(\n                                diamonds = userDiamonds,\n                                modifier = Modifier\n                            )\n                        }\n                    )\n                },\n                content = content\n            )\n        }\n    }\n}\n\n@Composable\n@Preview(\n    showBackground = true,\n    device = \"spec:width=673.5dp,height=841dp,dpi=480\",\n    group = \"Medium\"\n)\n@Preview(\n    showBackground = true,\n    uiMode = Configuration.UI_MODE_NIGHT_YES,\n    device = \"spec:width=673.5dp,height=841dp,dpi=480\",\n    group = \"Medium\"\n)\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun MediumContainerPreview() {\n    val otherItems = getOtherItems(dailyChallengeClaimCount = 5)\n\n    val selectedItem = otherItems\n        .filterIsInstance<NavigationItem.Item>()\n        .firstOrNull()\n\n    NewQuizTheme {\n        Surface {\n            MediumContainer(\n                navigator = EmptyDestinationsNavigator,\n                content = {\n                    Text(text = \"NewQuiz\")\n                },\n                primaryItems = getPrimaryItems(),\n                otherItems = otherItems,\n                selectedItem = selectedItem,\n                snackbarHostState = SnackbarHostState()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/navigation/NavDrawerContent.kt",
    "content": "package com.infinitepower.newquiz.ui.navigation\n\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.ColumnScope\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.itemsIndexed\nimport androidx.compose.material3.Badge\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.ModalDrawerSheet\nimport androidx.compose.material3.NavigationDrawerItem\nimport androidx.compose.material3.NavigationDrawerItemDefaults\nimport androidx.compose.material3.PermanentDrawerSheet\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport com.infinitepower.newquiz.core.navigation.NavigationItem\nimport com.infinitepower.newquiz.core.theme.spacing\nimport kotlinx.collections.immutable.ImmutableList\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun NavigationDrawerContent(\n    modifier: Modifier = Modifier,\n    onItemClick: (item: NavigationItem.Item) -> Unit,\n    permanent: Boolean = false,\n    items: ImmutableList<NavigationItem>,\n    selectedItem: NavigationItem.Item?\n) {\n    NavigationDrawerContainer(\n        modifier = modifier,\n        permanent = permanent\n    ) {\n        NavigationDrawerContent(\n            items = items,\n            selectedItem = selectedItem,\n            onItemClick = onItemClick,\n        )\n    }\n}\n\n@Composable\n@ExperimentalMaterial3Api\nprivate fun NavigationDrawerContainer(\n    modifier: Modifier = Modifier,\n    permanent: Boolean,\n    content: @Composable ColumnScope.() -> Unit\n) {\n    if (permanent) {\n        PermanentDrawerSheet(\n            modifier = modifier,\n            content = content\n        )\n    } else {\n        ModalDrawerSheet(\n            modifier = modifier,\n            content = content\n        )\n    }\n}\n\n@Composable\n@ExperimentalMaterial3Api\nprivate fun NavigationDrawerContent(\n    modifier: Modifier = Modifier,\n    onItemClick: (item: NavigationItem.Item) -> Unit,\n    items: ImmutableList<NavigationItem>,\n    selectedItem: NavigationItem.Item?,\n) {\n    LazyColumn(\n        modifier = modifier,\n        contentPadding = NavigationDrawerItemDefaults.ItemPadding\n    ) {\n        item {\n            Column(\n                modifier = Modifier.padding(vertical = MaterialTheme.spacing.large)\n            ) {\n                Text(\n                    text = stringResource(id = CoreR.string.app_name),\n                    style = MaterialTheme.typography.headlineSmall\n                )\n            }\n        }\n\n        itemsIndexed(items = items) { index, item ->\n            val text = stringResource(id = item.text)\n\n            when (item) {\n                is NavigationItem.Label -> {\n                    if (items.getOrNull(index - 1) is NavigationItem.Item) {\n                        Spacer(modifier = Modifier.height(MaterialTheme.spacing.medium))\n                    }\n                    NavigationDrawerLabel(text = text)\n                    Spacer(modifier = Modifier.height(MaterialTheme.spacing.medium))\n                }\n\n                is NavigationItem.Item -> {\n                    NavigationDrawerItem(\n                        icon = {\n                            Icon(\n                                imageVector = item.getIcon(item == selectedItem),\n                                contentDescription = text\n                            )\n                        },\n                        label = { Text(text = text) },\n                        selected = item == selectedItem,\n                        onClick = { onItemClick(item) },\n                        badge = if (item.badge != null && item.badge.value > 0) {\n                            {\n                                Badge {\n                                    Text(\n                                        text = item.badge.value.toString(),\n                                        modifier = Modifier.semantics {\n                                            contentDescription = item.badge.description\n                                        }\n                                    )\n                                }\n                            }\n                        } else {\n                            null\n                        }\n                    )\n                }\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun NavigationDrawerLabel(\n    text: String\n) {\n    Text(\n        text = text,\n        style = MaterialTheme.typography.labelLarge\n    )\n}\n"
  },
  {
    "path": "app/src/main/java/com/infinitepower/newquiz/ui/navigation/NavigationContainer.kt",
    "content": "package com.infinitepower.newquiz.ui.navigation\n\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.WindowInsets\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.automirrored.rounded.List\nimport androidx.compose.material.icons.automirrored.rounded.ListAlt\nimport androidx.compose.material.icons.outlined.Compare\nimport androidx.compose.material.icons.outlined.ViewModule\nimport androidx.compose.material.icons.rounded.Image\nimport androidx.compose.material.icons.rounded.Person\nimport androidx.compose.material.icons.rounded.Route\nimport androidx.compose.material.icons.rounded.Settings\nimport androidx.compose.material.icons.rounded.Today\nimport androidx.compose.material.icons.rounded.ViewModule\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.SnackbarHost\nimport androidx.compose.material3.SnackbarHostState\nimport androidx.compose.material3.SnackbarResult\nimport androidx.compose.material3.windowsizeclass.WindowWidthSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.navigation.NavController\nimport com.infinitepower.newquiz.comparison_quiz.destinations.ComparisonQuizListScreenDestination\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.navigation.NavDrawerBadgeItem\nimport com.infinitepower.newquiz.core.navigation.NavigationItem\nimport com.infinitepower.newquiz.core.navigation.ScreenType\nimport com.infinitepower.newquiz.core.ui.ObserveAsEvents\nimport com.infinitepower.newquiz.core.ui.SnackbarController\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.feature.daily_challenge.destinations.DailyChallengeScreenDestination\nimport com.infinitepower.newquiz.feature.maze.destinations.MazeScreenDestination\nimport com.infinitepower.newquiz.feature.profile.destinations.ProfileScreenDestination\nimport com.infinitepower.newquiz.feature.settings.destinations.SettingsScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizListScreenDestination\nimport com.infinitepower.newquiz.wordle.destinations.WordleListScreenDestination\nimport com.ramcosta.composedestinations.spec.DestinationSpec\nimport com.ramcosta.composedestinations.utils.currentDestinationAsState\nimport com.ramcosta.composedestinations.utils.rememberDestinationsNavigator\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\nimport kotlinx.coroutines.launch\n\ninternal fun getPrimaryItems(): ImmutableList<NavigationItem.Item> = persistentListOf(\n    NavigationItem.Item(\n        text = R.string.multi_choice_quiz,\n        selectedIcon = Icons.AutoMirrored.Rounded.ListAlt,\n        unselectedIcon = Icons.AutoMirrored.Rounded.List,\n        direction = MultiChoiceQuizListScreenDestination,\n        primary = true\n    ),\n    NavigationItem.Item(\n        text = R.string.wordle,\n        selectedIcon = Icons.Rounded.ViewModule,\n        unselectedIcon = Icons.Outlined.ViewModule,\n        direction = WordleListScreenDestination,\n        primary = true\n    ),\n    NavigationItem.Item(\n        text = R.string.comparison_quiz,\n        selectedIcon = Icons.Rounded.Image,\n        unselectedIcon = Icons.Outlined.Compare,\n        direction = ComparisonQuizListScreenDestination,\n        primary = true\n    ),\n)\n\ninternal fun getOtherItems(\n    dailyChallengeClaimCount: Int = 0\n): ImmutableList<NavigationItem> = persistentListOf(\n    NavigationItem.Item(\n        text = R.string.maze,\n        selectedIcon = Icons.Rounded.Route,\n        direction = MazeScreenDestination,\n        screenType = ScreenType.NAVIGATION_HIDDEN\n    ),\n    NavigationItem.Item(\n        text = R.string.daily_challenge,\n        selectedIcon = Icons.Rounded.Today,\n        direction = DailyChallengeScreenDestination,\n        screenType = ScreenType.NAVIGATION_HIDDEN,\n        badge = NavDrawerBadgeItem(\n            value = dailyChallengeClaimCount,\n            description = \"Daily challenge claim count\"\n        )\n    ),\n    NavigationItem.Label(text = R.string.user),\n    NavigationItem.Item(\n        text = R.string.profile,\n        selectedIcon = Icons.Rounded.Person,\n        direction = ProfileScreenDestination,\n        screenType = ScreenType.NAVIGATION_HIDDEN\n    ),\n    NavigationItem.Label(text = R.string.other),\n    NavigationItem.Item(\n        text = R.string.settings,\n        selectedIcon = Icons.Rounded.Settings,\n        direction = SettingsScreenDestination(),\n        screenType = ScreenType.NAVIGATION_HIDDEN\n    )\n)\n\nprivate fun List<NavigationItem>.getNavigationItemBy(\n    route: DestinationSpec<*>?\n): NavigationItem.Item? = filterIsInstance<NavigationItem.Item>()\n    .find { item -> item.direction == route }\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun NavigationContainer(\n    navController: NavController,\n    windowWidthSize: WindowWidthSizeClass,\n    dailyChallengeClaimCount: Int,\n    userDiamonds: UInt,\n    content: @Composable (PaddingValues) -> Unit\n) {\n    val scope = rememberCoroutineScope()\n\n    val navigator = navController.rememberDestinationsNavigator()\n    val destination by navController.currentDestinationAsState()\n\n    val primaryItems = remember { getPrimaryItems() }\n\n    val otherItems = remember(dailyChallengeClaimCount) {\n        getOtherItems(dailyChallengeClaimCount)\n    }\n\n    val selectedItem = remember(primaryItems, otherItems, destination) {\n        primaryItems.getNavigationItemBy(destination)\n            ?: otherItems.getNavigationItemBy(destination)\n    }\n\n    val navigationVisible = remember(selectedItem) {\n        selectedItem != null && selectedItem.screenType == ScreenType.NORMAL\n    }\n\n    val context = LocalContext.current\n\n    val snackbarHostState = remember { SnackbarHostState() }\n    ObserveAsEvents(flow = SnackbarController.events, snackbarHostState) { event ->\n        scope.launch {\n            snackbarHostState.currentSnackbarData?.dismiss()\n\n            val result = snackbarHostState.showSnackbar(\n                message = event.message.asString(context),\n                actionLabel = event.action?.name?.asString(context),\n                withDismissAction = event.withDismissAction,\n                duration = event.duration\n            )\n\n            if (result == SnackbarResult.ActionPerformed) {\n                event.action?.action?.invoke()\n            }\n        }\n    }\n\n    if (navigationVisible) {\n        when (windowWidthSize) {\n            WindowWidthSizeClass.Compact -> CompactContainer(\n                navigator = navigator,\n                navController = navController,\n                primaryItems = primaryItems,\n                otherItems = otherItems,\n                selectedItem = selectedItem,\n                userDiamonds = userDiamonds,\n                snackbarHostState = snackbarHostState,\n                content = content\n            )\n\n            WindowWidthSizeClass.Medium -> MediumContainer(\n                navigator = navigator,\n                primaryItems = primaryItems,\n                otherItems = otherItems,\n                selectedItem = selectedItem,\n                userDiamonds = userDiamonds,\n                snackbarHostState = snackbarHostState,\n                content = content\n            )\n\n            WindowWidthSizeClass.Expanded -> ExpandedContainer(\n                navigator = navigator,\n                primaryItems = primaryItems,\n                otherItems = otherItems,\n                selectedItem = selectedItem,\n                userDiamonds = userDiamonds,\n                snackbarHostState = snackbarHostState,\n                content = content\n            )\n        }\n    } else {\n        Scaffold(\n            content = content,\n            snackbarHost = {\n                SnackbarHost(hostState = snackbarHostState)\n            },\n            // This Scaffold only manages Snackbar display and shouldn't handle window insets.\n            // Insets are delegated to parent layouts or other Scaffolds in the composable hierarchy.\n            contentWindowInsets = WindowInsets(0, 0, 0, 0)\n        )\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/round_password_24.xml",
    "content": "<vector\n    android:height=\"24dp\"\n    android:tint=\"#000000\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"VectorPath\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M3,17h18c0.55,0 1,0.45 1,1v0c0,0.55 -0.45,1 -1,1H3c-0.55,0 -1,-0.45 -1,-1v0C2,17.45 2.45,17 3,17zM2.5,12.57c0.36,0.21 0.82,0.08 1.03,-0.28L4,11.47l0.48,0.83c0.21,0.36 0.67,0.48 1.03,0.28l0,0c0.36,-0.21 0.48,-0.66 0.28,-1.02L5.3,10.72h0.95C6.66,10.72 7,10.38 7,9.97v0c0,-0.41 -0.34,-0.75 -0.75,-0.75H5.3L5.77,8.4C5.98,8.04 5.86,7.58 5.5,7.37l0,0C5.14,7.17 4.68,7.29 4.47,7.65L4,8.47L3.53,7.65C3.32,7.29 2.86,7.17 2.5,7.37l0,0C2.14,7.58 2.02,8.04 2.23,8.4L2.7,9.22H1.75C1.34,9.22 1,9.56 1,9.97v0c0,0.41 0.34,0.75 0.75,0.75H2.7l-0.48,0.83C2.02,11.91 2.14,12.37 2.5,12.57L2.5,12.57zM10.5,12.57L10.5,12.57c0.36,0.21 0.82,0.08 1.03,-0.28L12,11.47l0.48,0.83c0.21,0.36 0.67,0.48 1.03,0.28l0,0c0.36,-0.21 0.48,-0.66 0.28,-1.02l-0.48,-0.83h0.95c0.41,0 0.75,-0.34 0.75,-0.75v0c0,-0.41 -0.34,-0.75 -0.75,-0.75H13.3l0.47,-0.82c0.21,-0.36 0.08,-0.82 -0.27,-1.03l0,0c-0.36,-0.21 -0.82,-0.08 -1.02,0.27L12,8.47l-0.47,-0.82c-0.21,-0.36 -0.67,-0.48 -1.02,-0.27l0,0c-0.36,0.21 -0.48,0.67 -0.27,1.03l0.47,0.82H9.75C9.34,9.22 9,9.56 9,9.97v0c0,0.41 0.34,0.75 0.75,0.75h0.95l-0.48,0.83C10.02,11.91 10.14,12.37 10.5,12.57zM23,9.97c0,-0.41 -0.34,-0.75 -0.75,-0.75H21.3l0.47,-0.82c0.21,-0.36 0.08,-0.82 -0.27,-1.03l0,0c-0.36,-0.21 -0.82,-0.08 -1.02,0.27L20,8.47l-0.47,-0.82c-0.21,-0.36 -0.67,-0.48 -1.02,-0.27l0,0c-0.36,0.21 -0.48,0.67 -0.27,1.03l0.47,0.82h-0.95C17.34,9.22 17,9.56 17,9.97v0c0,0.41 0.34,0.75 0.75,0.75h0.95l-0.48,0.83c-0.21,0.36 -0.08,0.82 0.28,1.02l0,0c0.36,0.21 0.82,0.08 1.03,-0.28L20,11.47l0.48,0.83c0.21,0.36 0.67,0.48 1.03,0.28l0,0c0.36,-0.21 0.48,-0.66 0.28,-1.02l-0.48,-0.83h0.95C22.66,10.72 23,10.38 23,9.97L23,9.97z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/round_play_circle_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM9.5,14.67V9.33c0,-0.79 0.88,-1.27 1.54,-0.84l4.15,2.67c0.61,0.39 0.61,1.29 0,1.68l-4.15,2.67C10.38,15.94 9.5,15.46 9.5,14.67z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/round_quiz_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M17,20H4V7c0,-0.55 -0.45,-1 -1,-1S2,6.45 2,7v13c0,1.1 0.9,2 2,2h13c0.55,0 1,-0.45 1,-1S17.55,20 17,20z\"/>\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M20,2H8C6.9,2 6,2.9 6,4v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4C22,2.9 21.1,2 20,2zM14.01,15c-0.59,0 -1.05,-0.47 -1.05,-1.05c0,-0.59 0.47,-1.04 1.05,-1.04c0.59,0 1.04,0.45 1.04,1.04C15.04,14.53 14.6,15 14.01,15zM16.51,8.83c-0.63,0.93 -1.23,1.21 -1.56,1.81c-0.08,0.14 -0.13,0.26 -0.16,0.49c-0.05,0.39 -0.36,0.68 -0.75,0.68h-0.03c-0.44,0 -0.79,-0.38 -0.75,-0.82c0.03,-0.28 0.09,-0.57 0.25,-0.84c0.41,-0.73 1.18,-1.16 1.63,-1.8c0.48,-0.68 0.21,-1.94 -1.14,-1.94c-0.61,0 -1.01,0.32 -1.26,0.7c-0.19,0.29 -0.57,0.39 -0.89,0.25l0,0c-0.42,-0.18 -0.6,-0.7 -0.34,-1.07C12.02,5.55 12.87,5 13.99,5c1.23,0 2.08,0.56 2.51,1.26C16.87,6.87 17.08,7.99 16.51,8.83z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/resources.properties",
    "content": "unqualifiedResLocale=en"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"black\">#FF000000</color>\n    <color name=\"white\">#FFFFFFFF</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/leak_canary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>"
  },
  {
    "path": "app/src/main/res/values/splash_screen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"Theme.NewQuiz.Starting\" parent=\"Theme.SplashScreen\">\n        <item name=\"windowSplashScreenBackground\">@color/white</item>\n        <item name=\"windowSplashScreenAnimatedIcon\">@mipmap/ic_launcher_round</item>\n        <item name=\"postSplashScreenTheme\">@style/Theme.NewQuiz</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\" translatable=\"false\">NewQuiz</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-night/splash_screen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"Theme.NewQuiz.Starting\" parent=\"Theme.SplashScreen\">\n        <item name=\"windowSplashScreenBackground\">@color/black</item>\n        <item name=\"windowSplashScreenAnimatedIcon\">@mipmap/ic_launcher_round</item>\n        <item name=\"postSplashScreenTheme\">@style/Theme.NewQuiz</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/xml-v25/shortcuts.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shortcuts xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <shortcut\n        android:shortcutId=\"quick_quiz\"\n        android:enabled=\"true\"\n        android:icon=\"@drawable/round_play_circle_24\"\n        android:shortcutShortLabel=\"@string/maze\"\n        android:shortcutLongLabel=\"@string/maze\">\n        <intent\n            android:action=\"android.intent.action.VIEW\"\n            android:targetPackage=\"com.infinitepower.newquiz\"\n            android:targetClass=\"com.infinitepower.newquiz.ui.main.MainActivity\"\n            android:data=\"newquiz://maze\"/>\n    </shortcut>\n\n    <shortcut\n        android:shortcutId=\"quick_quiz\"\n        android:enabled=\"true\"\n        android:icon=\"@drawable/round_quiz_24\"\n        android:shortcutShortLabel=\"@string/quick_quiz\"\n        android:shortcutLongLabel=\"@string/quick_quiz\">\n        <intent\n            android:action=\"android.intent.action.VIEW\"\n            android:targetPackage=\"com.infinitepower.newquiz\"\n            android:targetClass=\"com.infinitepower.newquiz.ui.main.MainActivity\"\n            android:data=\"newquiz://quickquiz\"/>\n    </shortcut>\n\n    <shortcut\n        android:shortcutId=\"wordle_infinite\"\n        android:enabled=\"true\"\n        android:icon=\"@drawable/round_password_24\"\n        android:shortcutShortLabel=\"@string/wordle_infinite\"\n        android:shortcutLongLabel=\"@string/wordle_infinite\">\n        <intent\n            android:action=\"android.intent.action.VIEW\"\n            android:targetPackage=\"com.infinitepower.newquiz\"\n            android:targetClass=\"com.infinitepower.newquiz.ui.main.MainActivity\"\n            android:data=\"newquiz://wordleinfinite\"/>\n    </shortcut>\n</shortcuts>"
  },
  {
    "path": "build-logic/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor.xml\n/.idea/assetWizardSettings.xml\n.DS_Store\n/build\n/convention/build\n/captures\n.externalNativeBuild\n.cxx\nlocal.properties\n"
  },
  {
    "path": "build-logic/README.md",
    "content": "# Build logic\n\nThe build-logic folder defines project-specific convention plugins, used to keep a single source of truth for common module configurations.\n\nBased in the [Now in Android](https://github.com/android/nowinandroid) project."
  },
  {
    "path": "build-logic/convention/build.gradle.kts",
    "content": "import org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nplugins {\n    `kotlin-dsl`\n    alias(libs.plugins.detekt)\n}\n\ngroup = \"com.infinitepower.newquiz.buildlogic\"\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_17\n    targetCompatibility = JavaVersion.VERSION_17\n}\n\nkotlin {\n    compilerOptions {\n        jvmTarget = JvmTarget.JVM_17\n    }\n}\n\ntasks {\n    validatePlugins {\n        enableStricterValidation = true\n        failOnWarning = true\n    }\n}\n\ndependencies {\n    compileOnly(libs.android.gradlePlugin)\n    compileOnly(libs.firebase.crashlytics.gradlePlugin)\n    compileOnly(libs.firebase.performance.gradlePlugin)\n    compileOnly(libs.kotlin.gradlePlugin)\n    compileOnly(libs.ksp.gradlePlugin)\n    compileOnly(libs.compose.gradlePlugin)\n    implementation(libs.detekt.gradlePlugin)\n}\n\ndetekt {\n    buildUponDefaultConfig = true\n    config.from(file(\"../../detekt.yml\"))\n}\n\ngradlePlugin {\n    plugins {\n        register(\"androidApplicationCompose\") {\n            id = \"newquiz.android.application.compose\"\n            implementationClass = \"AndroidApplicationComposeConventionPlugin\"\n        }\n        register(\"androidApplication\") {\n            id = \"newquiz.android.application\"\n            implementationClass = \"AndroidApplicationConventionPlugin\"\n        }\n        register(\"androidLibraryCompose\") {\n            id = \"newquiz.android.library.compose\"\n            implementationClass = \"AndroidLibraryComposeConventionPlugin\"\n        }\n        register(\"androidLibrary\") {\n            id = \"newquiz.android.library\"\n            implementationClass = \"AndroidLibraryConventionPlugin\"\n        }\n        register(\"androidHilt\") {\n            id = \"newquiz.android.hilt\"\n            implementationClass = \"AndroidHiltConventionPlugin\"\n        }\n        register(\"androidRoom\") {\n            id = \"newquiz.android.room\"\n            implementationClass = \"AndroidRoomConventionPlugin\"\n        }\n        register(\"androidFirebase\") {\n            id = \"newquiz.android.application.firebase\"\n            implementationClass = \"AndroidApplicationFirebaseConventionPlugin\"\n        }\n        register(\"jvmLibrary\") {\n            id = \"newquiz.jvm.library\"\n            implementationClass = \"JvmLibraryConventionPlugin\"\n        }\n        register(\"kotlinSerialization\") {\n            id = \"newquiz.kotlin.serialization\"\n            implementationClass = \"KotlinSerializationConventionPlugin\"\n        }\n        register(\"androidComposeDestinations\") {\n            id = \"newquiz.android.compose.destinations\"\n            implementationClass = \"AndroidComposeDestinationsConventionPlugin\"\n        }\n        register(\"androidFeature\") {\n            id = \"newquiz.android.feature\"\n            implementationClass = \"AndroidFeatureConventionPlugin\"\n        }\n        register(\"detekt\") {\n            id = \"newquiz.detekt\"\n            implementationClass = \"DetektConventionPlugin\"\n        }\n    }\n}"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt",
    "content": "import com.android.build.api.dsl.ApplicationExtension\nimport com.infinitepower.newquiz.configureAndroidCompose\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.getByType\n\nclass AndroidApplicationComposeConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            pluginManager.apply(\"com.android.application\")\n\n            val extension = extensions.getByType<ApplicationExtension>()\n            configureAndroidCompose(extension)\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt",
    "content": "import com.android.build.api.dsl.ApplicationExtension\nimport com.infinitepower.newquiz.NewQuizFlavor\nimport com.infinitepower.newquiz.ProjectConfig\nimport com.infinitepower.newquiz.configureFlavors\nimport com.infinitepower.newquiz.configureKotlinAndroid\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.apply\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.support.uppercaseFirstChar\n\nclass AndroidApplicationConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            with(pluginManager) {\n                apply(\"com.android.application\")\n                apply(\"org.jetbrains.kotlin.android\")\n            }\n\n            extensions.configure<ApplicationExtension> {\n                configureKotlinAndroid(this)\n                configureFlavors(this)\n\n                defaultConfig {\n                    targetSdk = ProjectConfig.targetSdk\n                }\n            }\n\n            // Apply the Firebase plugin only for the \"Normal\" build type\n            val gradleTaskRequests = gradle.startParameter.taskRequests.toString()\n            val normalFlavor = NewQuizFlavor.normal.name.uppercaseFirstChar()\n\n            // Check if the flavor name is in the gradle task requests,\n            // like \"assembleNormalDebug\" or \"assembleFossDebug\"\n            if (gradleTaskRequests.contains(normalFlavor)) {\n                apply(plugin = \"newquiz.android.application.firebase\")\n                logger.info(\"Applied Firebase plugin for normal build type\")\n            }\n\n            tasks.register(\"testAllUnitTest\") {\n                // Only run debug tests\n                dependsOn(\n                    getTasksByName(\"testNormalDebugUnitTest\", true),\n                    getTasksByName(\"testFossDebugUnitTest\", true),\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt",
    "content": "import com.infinitepower.newquiz.implementation\nimport com.infinitepower.newquiz.libs\nimport com.infinitepower.newquiz.normalImplementation\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.dependencies\n\nclass AndroidApplicationFirebaseConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            with(pluginManager) {\n                apply(\"com.google.gms.google-services\")\n                apply(\"com.google.firebase.firebase-perf\")\n                apply(\"com.google.firebase.crashlytics\")\n            }\n\n            dependencies {\n                val bom = libs.findLibrary(\"firebase-bom\").get()\n\n                implementation(platform(bom))\n                normalImplementation(libs.findLibrary(\"firebase-analytics\").get())\n                normalImplementation(libs.findLibrary(\"firebase-crashlytics\").get())\n                normalImplementation(libs.findLibrary(\"firebase-perf\").get())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidComposeDestinationsConventionPlugin.kt",
    "content": "import com.google.devtools.ksp.gradle.KspExtension\nimport com.infinitepower.newquiz.implementation\nimport com.infinitepower.newquiz.ksp\nimport com.infinitepower.newquiz.libs\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.dependencies\n\nclass AndroidComposeDestinationsConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            plugins.apply(\"com.google.devtools.ksp\")\n\n            dependencies {\n                implementation(libs.findLibrary(\"compose.destinations.core\").get())\n                ksp(libs.findLibrary(\"compose.destinations.ksp\").get())\n            }\n\n            extensions.configure<KspExtension> {\n                val mode = \"destinations\"\n                val moduleName = target.name\n\n                logger.info(\"Configuring compose-destinations for $moduleName, with mode: $mode\")\n\n                arg(\"compose-destinations.mode\", mode)\n                arg(\"compose-destinations.moduleName\", moduleName)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt",
    "content": "import com.infinitepower.newquiz.debugImplementation\nimport com.infinitepower.newquiz.implementation\nimport com.infinitepower.newquiz.libs\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.dependencies\n\nclass AndroidFeatureConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            pluginManager.apply {\n                apply(\"newquiz.android.library.compose\")\n                apply(\"newquiz.android.hilt\")\n            }\n\n            dependencies {\n                implementation(project(\":model\"))\n                implementation(project(\":core\"))\n                implementation(project(\":core:analytics\"))\n\n                implementation(libs.findLibrary(\"androidx.compose.foundation\").get())\n                implementation(libs.findLibrary(\"androidx.compose.material3\").get())\n                implementation(libs.findLibrary(\"androidx.compose.runtime\").get())\n                implementation(libs.findLibrary(\"androidx.compose.ui.tooling.preview\").get())\n\n                debugImplementation(libs.findLibrary(\"androidx.compose.ui.tooling\").get())\n\n                implementation(libs.findLibrary(\"hilt.navigationCompose\").get())\n                implementation(libs.findLibrary(\"androidx.lifecycle.runtimeCompose\").get())\n                implementation(libs.findLibrary(\"androidx.lifecycle.viewModelCompose\").get())\n\n                implementation(libs.findLibrary(\"kotlinx.coroutines.android\").get())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt",
    "content": "import com.infinitepower.newquiz.androidTestImplementation\nimport com.infinitepower.newquiz.implementation\nimport com.infinitepower.newquiz.ksp\nimport com.infinitepower.newquiz.kspAndroidTest\nimport com.infinitepower.newquiz.libs\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.dependencies\n\nclass AndroidHiltConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            with(pluginManager) {\n                apply(\"dagger.hilt.android.plugin\")\n                apply(\"com.google.devtools.ksp\")\n            }\n\n            dependencies {\n                // Base dependencies of hilt\n                implementation(libs.findLibrary(\"hilt.android\").get())\n                ksp(libs.findLibrary(\"hilt.compiler\").get())\n\n                // Dependencies for testing\n                kspAndroidTest(libs.findLibrary(\"hilt.compiler\").get())\n                androidTestImplementation(libs.findLibrary(\"hilt.android.testing\").get())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt",
    "content": "import com.android.build.api.dsl.LibraryExtension\nimport com.infinitepower.newquiz.configureAndroidCompose\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.configure\n\nclass AndroidLibraryComposeConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            with(pluginManager) {\n                apply(\"newquiz.android.library\")\n            }\n\n            extensions.configure<LibraryExtension> {\n                configureAndroidCompose(this)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt",
    "content": "import com.android.build.api.dsl.LibraryExtension\nimport com.android.build.api.variant.LibraryAndroidComponentsExtension\nimport com.infinitepower.newquiz.ProjectConfig\nimport com.infinitepower.newquiz.androidTestImplementation\nimport com.infinitepower.newquiz.configureFlavors\nimport com.infinitepower.newquiz.configureKotlinAndroid\nimport com.infinitepower.newquiz.disableUnnecessaryAndroidTests\nimport com.infinitepower.newquiz.libs\nimport com.infinitepower.newquiz.testImplementation\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.tasks.testing.Test\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.dependencies\nimport org.gradle.kotlin.dsl.kotlin\nimport org.gradle.kotlin.dsl.withType\n\nclass AndroidLibraryConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            with(pluginManager) {\n                apply(\"com.android.library\")\n                apply(\"org.jetbrains.kotlin.android\")\n            }\n\n            extensions.configure<LibraryExtension> {\n                configureKotlinAndroid(this)\n                configureFlavors(this)\n\n                defaultConfig {\n                    targetSdk = ProjectConfig.targetSdk\n                    testInstrumentationRunner = \"com.infinitepower.newquiz.core.testing.NewQuizTestRunner\"\n                }\n            }\n\n            extensions.configure<LibraryAndroidComponentsExtension> {\n                disableUnnecessaryAndroidTests(target)\n            }\n\n            dependencies {\n                // Test libraries\n                testImplementation(kotlin(\"test\"))\n                testImplementation(libs.findLibrary(\"google.truth\").get())\n                testImplementation(libs.findLibrary(\"mockk\").get())\n                testImplementation(libs.findLibrary(\"kotlinx.coroutines.test\").get())\n                testImplementation(libs.findLibrary(\"junit.jupiter.params\").get())\n                testImplementation(libs.findLibrary(\"turbine\").get())\n\n                androidTestImplementation(kotlin(\"test\"))\n                androidTestImplementation(libs.findLibrary(\"google.truth\").get())\n                androidTestImplementation(libs.findLibrary(\"mockk.android\").get())\n                androidTestImplementation(libs.findLibrary(\"kotlinx.coroutines.test\").get())\n                androidTestImplementation(libs.findLibrary(\"androidx.test.runner\").get())\n                androidTestImplementation(libs.findLibrary(\"androidx.test.rules\").get())\n                androidTestImplementation(libs.findLibrary(\"androidx.compose.ui.test\").get())\n                androidTestImplementation(libs.findLibrary(\"turbine\").get())\n                androidTestImplementation(project(\":core:testing\"))\n\n                // Fix for problem of duplicate classes with guava\n                modules {\n                    module(\"com.google.guava:listenablefuture\") {\n                        replacedBy(\"com.google.guava:guava\", \"listenablefuture is part of guava\")\n                    }\n                }\n            }\n\n            tasks.withType<Test> {\n                useJUnitPlatform()\n            }\n\n            tasks.register(\"testAllUnitTest\") {\n                // Only run debug tests\n                dependsOn(\n                    getTasksByName(\"testNormalDebugUnitTest\", true),\n                    getTasksByName(\"testFossDebugUnitTest\", true),\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt",
    "content": "import com.google.devtools.ksp.gradle.KspExtension\nimport com.infinitepower.newquiz.implementation\nimport com.infinitepower.newquiz.ksp\nimport com.infinitepower.newquiz.libs\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.tasks.InputDirectory\nimport org.gradle.api.tasks.PathSensitive\nimport org.gradle.api.tasks.PathSensitivity\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.dependencies\nimport org.gradle.process.CommandLineArgumentProvider\nimport java.io.File\n\nclass AndroidRoomConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            pluginManager.apply(\"com.google.devtools.ksp\")\n\n            extensions.configure<KspExtension> {\n                // The schemas directory contains a schema file for each version of the Room database.\n                // This is required to enable Room auto migrations.\n                // See https://developer.android.com/reference/kotlin/androidx/room/AutoMigration.\n                arg(RoomSchemaArgProvider(File(projectDir, \"schemas\")))\n            }\n\n            dependencies {\n                implementation(libs.findLibrary(\"room.runtime\").get())\n                implementation(libs.findLibrary(\"room.ktx\").get())\n                ksp(libs.findLibrary(\"room.compiler\").get())\n            }\n        }\n    }\n\n    class RoomSchemaArgProvider(\n        @get:InputDirectory\n        @get:PathSensitive(PathSensitivity.RELATIVE)\n        val schemaDir: File,\n    ) : CommandLineArgumentProvider {\n        override fun asArguments() = listOf(\"room.schemaLocation=${schemaDir.path}\")\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/DetektConventionPlugin.kt",
    "content": "import com.infinitepower.newquiz.libs\nimport io.gitlab.arturbosch.detekt.Detekt\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.dependencies\nimport org.gradle.kotlin.dsl.withType\n\nclass DetektConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            pluginManager.apply(\n                libs.findLibrary(\"detekt.gradlePlugin\").get().get().group.toString()\n            )\n\n            tasks.withType<Detekt> {\n                buildUponDefaultConfig = true\n                basePath = target.rootProject.projectDir.absolutePath\n\n                val localDetektConfig = target.file(\"detekt.yml\")\n                val rootDetektConfig = target.rootProject.file(\"detekt.yml\")\n                val rootDetektComposeConfig = target.rootProject.file(\"detekt-compose.yml\")\n                if (localDetektConfig.exists()) {\n                    config.from(\n                        localDetektConfig,\n                        rootDetektConfig,\n                        rootDetektComposeConfig\n                    )\n                } else {\n                    config.from(rootDetektConfig, rootDetektComposeConfig)\n                }\n\n                reports {\n                    sarif.required.set(true)\n                }\n            }\n\n            dependencies {\n                add(\"detektPlugins\", libs.findLibrary(\"detekt.compose\").get())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt",
    "content": "import com.infinitepower.newquiz.configureKotlinJvm\nimport com.infinitepower.newquiz.libs\nimport com.infinitepower.newquiz.testImplementation\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.tasks.testing.Test\nimport org.gradle.kotlin.dsl.dependencies\nimport org.gradle.kotlin.dsl.kotlin\nimport org.gradle.kotlin.dsl.register\nimport org.gradle.kotlin.dsl.withType\n\nclass JvmLibraryConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            with(pluginManager) {\n                apply(\"org.jetbrains.kotlin.jvm\") // kotlin(\"jvm\")\n            }\n\n            configureKotlinJvm()\n\n            dependencies {\n                // Test libraries\n                testImplementation(kotlin(\"test\"))\n                testImplementation(libs.findLibrary(\"google.truth\").get())\n                testImplementation(libs.findLibrary(\"mockk\").get())\n                testImplementation(libs.findLibrary(\"kotlinx.coroutines.test\").get())\n                testImplementation(libs.findLibrary(\"junit.jupiter.params\").get())\n                testImplementation(libs.findLibrary(\"turbine\").get())\n            }\n\n            tasks.withType<Test> {\n                useJUnitPlatform()\n            }\n\n\n            tasks.register<Test>(\"testAllUnitTest\") {\n                dependsOn(\n                    getTasksByName(\"test\", true),\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/KotlinSerializationConventionPlugin.kt",
    "content": "import com.infinitepower.newquiz.implementation\nimport com.infinitepower.newquiz.libs\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.dependencies\n\nclass KotlinSerializationConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            plugins.apply(\"kotlinx-serialization\")\n\n            dependencies {\n                implementation(libs.findLibrary(\"kotlinx.serialization.json\").get())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/infinitepower/newquiz/AndroidCompose.kt",
    "content": "package com.infinitepower.newquiz\n\nimport com.android.build.api.dsl.CommonExtension\nimport org.gradle.api.Project\nimport org.gradle.api.provider.Provider\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.dependencies\nimport org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension\n\ninternal fun Project.configureAndroidCompose(\n    applicationExtension: CommonExtension<*, *, *, *, *, *>\n) {\n    pluginManager.apply(\"org.jetbrains.kotlin.plugin.compose\")\n\n    applicationExtension.apply {\n        buildFeatures {\n            compose = true\n        }\n\n        dependencies {\n            val bom = libs.findLibrary(\"androidx-compose-bom\").get()\n            implementation(platform(bom))\n            androidTestImplementation(platform(bom))\n            implementation(libs.findLibrary(\"androidx-compose-ui-tooling-preview\").get())\n            // Add ComponentActivity to debug manifest\n            debugImplementation(libs.findLibrary(\"androidx-compose-ui-tooling\").get())\n        }\n\n        testOptions {\n            unitTests {\n                isIncludeAndroidResources = true\n            }\n        }\n    }\n\n    extensions.configure<ComposeCompilerGradlePluginExtension> {\n        fun Provider<String>.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } }\n        fun Provider<*>.relativeToRootProject(dir: String) = flatMap {\n            rootProject.layout.buildDirectory.dir(projectDir.toRelativeString(rootDir))\n        }.map { it.dir(dir) }\n\n        project.providers.gradleProperty(\"enableComposeCompilerMetrics\").onlyIfTrue()\n            .relativeToRootProject(\"compose-metrics\")\n            .let(metricsDestination::set)\n\n        project.providers.gradleProperty(\"enableComposeCompilerReports\").onlyIfTrue()\n            .relativeToRootProject(\"compose-reports\")\n            .let(reportsDestination::set)\n\n        stabilityConfigurationFile.set(rootProject.layout.projectDirectory.file(\"compose_compiler_config.conf\"))\n\n        enableStrongSkippingMode.set(true)\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/infinitepower/newquiz/AndroidInstrumentedTests.kt",
    "content": "package com.infinitepower.newquiz\n\nimport com.android.build.api.variant.LibraryAndroidComponentsExtension\nimport org.gradle.api.Project\n\n/**\n * Disable android tests if there is no androidTest folder.\n */\ninternal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests(\n    project: Project,\n) = beforeVariants {\n    it.enableAndroidTest = it.enableAndroidTest\n            && project.projectDir.resolve(\"src/androidTest\").exists()\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/infinitepower/newquiz/Flavors.kt",
    "content": "package com.infinitepower.newquiz\n\nimport com.android.build.api.dsl.ApplicationExtension\nimport com.android.build.api.dsl.ApplicationProductFlavor\nimport com.android.build.api.dsl.CommonExtension\nimport com.android.build.api.dsl.ProductFlavor\n\n@Suppress(\"EnumEntryName\", \"EnumNaming\")\nenum class FlavorDimension {\n    distribution,\n    // translation\n}\n\n@Suppress(\"EnumEntryName\", \"EnumNaming\")\nenum class NewQuizFlavor(\n    val dimension: FlavorDimension,\n    val applicationIdSuffix: String? = null\n) {\n    normal(FlavorDimension.distribution),\n    foss(FlavorDimension.distribution),\n\n    // translation(FlavorDimension.translation),\n    // noTranslation(FlavorDimension.translation),\n}\n\nfun configureFlavors(\n    commonExtension: CommonExtension<*, *, *, *, *, *>,\n    flavorConfigurationBlock: ProductFlavor.(flavor: NewQuizFlavor) -> Unit = {}\n) {\n    commonExtension.apply {\n        flavorDimensions += FlavorDimension.values().map { it.name }\n\n        productFlavors {\n            NewQuizFlavor.values().forEach { flavor ->\n                create(flavor.name) {\n                    dimension = flavor.dimension.name\n                    flavorConfigurationBlock(this, flavor)\n\n                    if (this@apply is ApplicationExtension && this is ApplicationProductFlavor) {\n                        if (flavor.applicationIdSuffix != null) {\n                            applicationIdSuffix = flavor.applicationIdSuffix\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/infinitepower/newquiz/KotlinAndroid.kt",
    "content": "package com.infinitepower.newquiz\n\nimport com.android.build.api.dsl.CommonExtension\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPluginExtension\nimport org.gradle.kotlin.dsl.assign\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.dependencies\nimport org.gradle.kotlin.dsl.withType\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension\nimport org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\ninternal fun Project.configureKotlinAndroid(\n    commonExtension: CommonExtension<*, * , * , *, *, *>\n) {\n    commonExtension.apply {\n        compileSdk = ProjectConfig.compileSdk\n\n        defaultConfig {\n            minSdk = ProjectConfig.minSdk\n        }\n\n        compileOptions {\n            sourceCompatibility = ProjectConfig.javaVersionCompatibility\n            targetCompatibility = ProjectConfig.javaVersionCompatibility\n\n            isCoreLibraryDesugaringEnabled = true\n        }\n\n        packaging {\n            resources {\n                excludes += \"/META-INF/{AL2.0,LGPL2.1}\"\n                excludes += \"/META-INF/LICENSE.md\"\n                excludes += \"/META-INF/LICENSE-notice.md\"\n            }\n        }\n    }\n\n    configureKotlinAndroid()\n\n    dependencies {\n        coreLibraryDesugaring(libs.findLibrary(\"android.desugarJdkLibs\").get())\n    }\n}\n\ninternal fun Project.configureKotlinJvm() {\n    extensions.apply {\n        configure<JavaPluginExtension> {\n            sourceCompatibility = ProjectConfig.javaVersionCompatibility\n            targetCompatibility = ProjectConfig.javaVersionCompatibility\n        }\n\n        configure<KotlinJvmProjectExtension> {\n            jvmToolchain(ProjectConfig.jvmToolchainVersion)\n        }\n    }\n}\n\nprivate fun Project.configureKotlinAndroid() {\n    tasks.withType<KotlinCompile>().configureEach {\n        compilerOptions {\n            jvmTarget = JvmTarget.JVM_17\n\n            freeCompilerArgs.add(\"-opt-in=kotlin.RequiresOptIn\")\n        }\n    }\n\n    extensions.configure<KotlinProjectExtension> {\n        jvmToolchain(ProjectConfig.jvmToolchainVersion)\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/infinitepower/newquiz/ProjectConfig.kt",
    "content": "package com.infinitepower.newquiz\n\nimport org.gradle.api.JavaVersion\n\nobject ProjectConfig {\n    const val compileSdk = 35\n\n    const val minSdk = 21\n\n    const val targetSdk = 35\n\n    val javaVersionCompatibility = JavaVersion.VERSION_17\n\n    const val jvmTargetVersion = \"17\"\n\n    const val jvmToolchainVersion = 17\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/infinitepower/newquiz/ProjectExtensions.kt",
    "content": "package com.infinitepower.newquiz\n\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.VersionCatalog\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport org.gradle.kotlin.dsl.getByType\n\nval Project.libs\n    get(): VersionCatalog = extensions.getByType<VersionCatalogsExtension>().named(\"libs\")\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/infinitepower/newquiz/Utils.kt",
    "content": "package com.infinitepower.newquiz\n\nimport org.gradle.api.artifacts.dsl.DependencyHandler\n\ninternal fun DependencyHandler.implementation(dependencyNotation: Any) =\n    add(\"implementation\", dependencyNotation)\n\ninternal fun DependencyHandler.testImplementation(dependencyNotation: Any) =\n    add(\"testImplementation\", dependencyNotation)\n\ninternal fun DependencyHandler.androidTestImplementation(dependencyNotation: Any) =\n    add(\"androidTestImplementation\", dependencyNotation)\n\ninternal fun DependencyHandler.ksp(dependencyNotation: Any) =\n    add(\"ksp\", dependencyNotation)\n\ninternal fun DependencyHandler.kspAndroidTest(dependencyNotation: Any) =\n    add(\"kspAndroidTest\", dependencyNotation)\n\ninternal fun DependencyHandler.normalImplementation(dependencyNotation: Any) =\n    add(\"normalImplementation\", dependencyNotation)\n\ninternal fun DependencyHandler.coreLibraryDesugaring(dependencyNotation: Any) =\n    add(\"coreLibraryDesugaring\", dependencyNotation)\n\ninternal fun DependencyHandler.debugImplementation(dependencyNotation: Any) =\n    add(\"debugImplementation\", dependencyNotation)\n"
  },
  {
    "path": "build-logic/gradle.properties",
    "content": "org.gradle.parallel=true\norg.gradle.caching=true\norg.gradle.configureondemand=true\n"
  },
  {
    "path": "build-logic/settings.gradle.kts",
    "content": "dependencyResolutionManagement {\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    versionCatalogs {\n        create(\"libs\") {\n            from(files(\"../gradle/libs.versions.toml\"))\n        }\n    }\n}\n\nrootProject.name = \"build-logic\"\ninclude(\":convention\")\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath(libs.google.oss.licenses.plugin) {\n            exclude(group = \"com.google.protobuf\")\n        }\n    }\n}\n\nplugins {\n    alias(libs.plugins.android.application) apply false\n    alias(libs.plugins.kotlin.jvm) apply false\n    alias(libs.plugins.kotlin.serialization) apply false\n    alias(libs.plugins.firebase.crashlytics) apply false\n    alias(libs.plugins.firebase.perf) apply false\n    alias(libs.plugins.gms) apply false\n    alias(libs.plugins.hilt) apply false\n    alias(libs.plugins.ksp) apply false\n    alias(libs.plugins.compose.compiler) apply false\n    id(\"com.github.ben-manes.versions\") version \"0.51.0\"\n}\n\ntasks.register(\"clean\", Delete::class) {\n    delete(layout.buildDirectory)\n}\n\ntasks.withType<DependencyUpdatesTask> {\n    rejectVersionIf {\n        // Don't allow non-stable versions, unless we are already using one for this dependency\n        isNonStable(candidate.version) && !isNonStable(currentVersion)\n    }\n}\n\n/**\n * Decides if this version is stable or not.\n */\nfun isNonStable(version: String): Boolean {\n    val stableKeyword = listOf(\"RELEASE\", \"FINAL\", \"GA\").any { version.uppercase().contains(it) }\n    val regex = \"^[0-9,.v-]+(-r)?$\".toRegex()\n    val isStable = stableKeyword || regex.matches(version)\n    return !isStable\n}\n"
  },
  {
    "path": "comparison-quiz/.gitignore",
    "content": "/build"
  },
  {
    "path": "comparison-quiz/README.md",
    "content": "# Comparison quiz\n\nThis is the code for the comparison game mode.\n\n![NewQuiz purple light](../pictures/comparison_quiz.jpg)"
  },
  {
    "path": "comparison-quiz/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library.compose)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.comparison_quiz\"\n}\n\ndependencies {\n    implementation(libs.androidx.core.ktx)\n\n    implementation(libs.androidx.lifecycle.livedata.ktx)\n    implementation(libs.androidx.lifecycle.runtimeCompose)\n\n    implementation(libs.androidx.compose.ui.tooling)\n    implementation(libs.androidx.compose.ui.tooling.preview)\n    implementation(libs.androidx.compose.material3)\n    implementation(libs.androidx.compose.material3.windowSizeClass)\n    implementation(libs.androidx.compose.material.iconsExtended)\n    debugImplementation(libs.androidx.compose.ui.testManifest)\n    implementation(libs.androidx.constraintlayout.compose)\n\n    androidTestImplementation(libs.androidx.compose.ui.test)\n    androidTestImplementation(libs.androidx.test.runner)\n    androidTestImplementation(libs.androidx.test.rules)\n\n    implementation(libs.hilt.navigationCompose)\n\n    implementation(libs.kotlinx.serialization.json)\n\n    implementation(libs.coil.kt.compose)\n    implementation(libs.coil.kt.svg)\n\n    implementation(libs.ktor.client.core)\n    implementation(libs.ktor.client.okhttp)\n    implementation(libs.ktor.client.serialization)\n\n    implementation(libs.androidx.work.ktx)\n    androidTestImplementation(libs.androidx.work.testing)\n\n    implementation(libs.lottie.compose)\n\n    // Hilt work manager\n    implementation(libs.hilt.ext.work)\n    ksp(libs.hilt.ext.compiler)\n\n    implementation(projects.core)\n    implementation(projects.core.analytics)\n    implementation(projects.core.remoteConfig)\n    implementation(projects.core.userServices)\n    implementation(projects.model)\n    implementation(projects.domain)\n    implementation(projects.data)\n    testImplementation(projects.core.testing)\n    androidTestImplementation(projects.core.testing)\n}\n\ntasks.withType<Test> {\n    useJUnitPlatform()\n}\n\nksp {\n    arg(\"compose-destinations.mode\", \"destinations\")\n    arg(\"compose-destinations.moduleName\", \"comparison-quiz\")\n}\n"
  },
  {
    "path": "comparison-quiz/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "comparison-quiz/proguard-rules.pro",
    "content": "# Temporary fix\n-dontwarn java.lang.invoke.StringConcatFactory"
  },
  {
    "path": "comparison-quiz/src/androidTest/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <application>\n        <activity\n            android:name=\"androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity\"\n            android:exported=\"true\"\n            tools:node=\"merge\">\n\n            <intent-filter tools:node=\"removeAll\" />\n        </activity>\n\n        <activity\n            android:name=\"androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity\"\n            android:exported=\"true\"\n            tools:node=\"merge\">\n\n            <intent-filter tools:node=\"removeAll\" />\n        </activity>\n\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            tools:node=\"remove\" />\n    </application>\n</manifest>"
  },
  {
    "path": "comparison-quiz/src/androidTest/java/com/infinitepower/newquiz/comparison_quiz/data/comparison_quiz/FakeComparisonQuizRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.data.comparison_quiz\n\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.model.FlowResource\nimport com.infinitepower.newquiz.model.Resource\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.flow.flow\nimport kotlinx.coroutines.flow.flowOf\nimport kotlinx.coroutines.flow.map\nimport kotlin.random.Random\n\nclass FakeComparisonQuizRepositoryImpl(\n    private val initialQuestions: List<ComparisonQuizItem> = emptyList(),\n    private val initialCategories: List<ComparisonQuizCategory> = emptyList()\n) : ComparisonQuizRepository {\n\n    private val questions = mutableListOf<ComparisonQuizItem>()\n\n    private val categories = mutableListOf<ComparisonQuizCategory>()\n\n    private val highestPosition = MutableStateFlow(0)\n\n    override fun getCategories(): List<ComparisonQuizCategory> = categories.toList()\n\n    init {\n        questions.addAll(initialQuestions)\n        categories.addAll(initialCategories)\n    }\n\n    override fun getQuestions(\n        category: ComparisonQuizCategory,\n        size: Int,\n        random: Random\n    ): Flow<List<ComparisonQuizItem>> = flowOf(questions.take(size))\n\n    override suspend fun getHighestPosition(categoryId: String): Int {\n        return highestPosition.first()\n    }\n\n    override fun getHighestPositionFlow(categoryId: String): Flow<Int> {\n        return highestPosition.map { it }\n    }\n}"
  },
  {
    "path": "comparison-quiz/src/androidTest/java/com/infinitepower/newquiz/comparison_quiz/list/components/ComparisonModeComponentTest.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.list.components\n\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertHasClickAction\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.assertIsEnabled\nimport androidx.compose.ui.test.assertIsNotEnabled\nimport androidx.compose.ui.test.assertIsNotSelected\nimport androidx.compose.ui.test.assertIsSelected\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithContentDescription\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme\nimport com.infinitepower.newquiz.core.testing.utils.setTestDeviceLocale\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class ComparisonModeComponentTest {\n    @get:Rule\n    val componentRule = createComposeRule()\n\n    @Test\n    fun comparisonModeComponent_greater_shouldDisplayTitleAndIcon() {\n        componentRule.setContent {\n            com.infinitepower.newquiz.core.testing.utils.setTestDeviceLocale()\n\n            com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme {\n                ComparisonModeComponent(\n                    selected = true,\n                    mode = ComparisonMode.GREATER\n                )\n            }\n        }\n\n        // Assert that the title is displayed and has the correct text\n        componentRule\n            .onNodeWithText(\"Greater\")\n            .assertExists()\n            .assertIsDisplayed()\n\n        // Assert that the icon is displayed and has the correct content description\n        componentRule\n            .onNodeWithContentDescription(\"Icon of Greater\")\n            .assertExists()\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun comparisonModeComponent_lesser_shouldDisplayTitleAndIcon() {\n        componentRule.setContent {\n            com.infinitepower.newquiz.core.testing.utils.setTestDeviceLocale()\n\n            com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme {\n                ComparisonModeComponent(\n                    selected = true,\n                    mode = ComparisonMode.LESSER\n                )\n            }\n        }\n\n        // Assert that the title is displayed and has the correct text\n        componentRule\n            .onNodeWithText(\"Lesser\")\n            .assertExists()\n            .assertIsDisplayed()\n\n        // Assert that the icon is displayed and has the correct content description\n        componentRule\n            .onNodeWithContentDescription(\"Icon of Lesser\")\n            .assertExists()\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun comparisonModeComponent_shouldInvokeOnClick_whenEnabled() {\n        var clicked = false\n\n        componentRule.setContent {\n            com.infinitepower.newquiz.core.testing.utils.setTestDeviceLocale()\n\n            com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme {\n                ComparisonModeComponent(\n                    selected = true,\n                    enabled = true,\n                    mode = ComparisonMode.LESSER,\n                    onClick = { clicked = true },\n                    modifier = Modifier.testTag(\"ComparisonModeComponent\")\n                )\n            }\n        }\n\n        // Click on the Composable\n        componentRule\n            .onNodeWithTag(\"ComparisonModeComponent\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertHasClickAction()\n            .performClick()\n\n        // Assert that the onClick callback was invoked\n        assertThat(clicked).isTrue()\n    }\n\n    @Test\n    fun comparisonModeComponent_shouldNotInvokeOnClick_whenNotEnabled() {\n        var clicked = false\n\n        componentRule.setContent {\n            com.infinitepower.newquiz.core.testing.utils.setTestDeviceLocale()\n\n            com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme {\n                ComparisonModeComponent(\n                    selected = true,\n                    enabled = false,\n                    mode = ComparisonMode.LESSER,\n                    onClick = { clicked = true },\n                    modifier = Modifier.testTag(\"ComparisonModeComponent\")\n                )\n            }\n        }\n\n        // Click on the Composable\n        componentRule\n            .onNodeWithTag(\"ComparisonModeComponent\")\n            .assertIsDisplayed()\n            .assertIsNotEnabled()\n            .performClick()\n\n        // Assert that the onClick callback was invoked\n        assertThat(clicked).isFalse()\n    }\n\n    @Test\n    fun comparisonModeComponent_shouldBeSelected() {\n        componentRule.setContent {\n            com.infinitepower.newquiz.core.testing.utils.setTestDeviceLocale()\n\n            com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme {\n                ComparisonModeComponent(\n                    selected = true,\n                    mode = ComparisonMode.LESSER,\n                    modifier = Modifier.testTag(\"ComparisonModeComponent\")\n                )\n            }\n        }\n\n        componentRule\n            .onNodeWithTag(\"ComparisonModeComponent\")\n            .assertIsDisplayed()\n            .assertIsSelected()\n    }\n\n    @Test\n    fun comparisonModeComponent_should_not_beSelected() {\n        componentRule.setContent {\n            com.infinitepower.newquiz.core.testing.utils.setTestDeviceLocale()\n\n            com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme {\n                ComparisonModeComponent(\n                    selected = false,\n                    mode = ComparisonMode.LESSER,\n                    modifier = Modifier.testTag(\"ComparisonModeComponent\")\n                )\n            }\n        }\n\n        componentRule\n            .onNodeWithTag(\"ComparisonModeComponent\")\n            .assertIsDisplayed()\n            .assertIsNotSelected()\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/androidTest/java/com/infinitepower/newquiz/comparison_quiz/list/components/ComparisonModeComponentsTest.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.list.components\n\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertCountEquals\nimport androidx.compose.ui.test.assertHasClickAction\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.assertIsEnabled\nimport androidx.compose.ui.test.assertIsNotSelected\nimport androidx.compose.ui.test.assertIsSelected\nimport androidx.compose.ui.test.assertTextEquals\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onChildren\nimport androidx.compose.ui.test.onFirst\nimport androidx.compose.ui.test.onLast\nimport androidx.compose.ui.test.onNodeWithContentDescription\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class ComparisonModeComponentsTest {\n    @get:Rule\n    val componentRule = createComposeRule()\n\n    @Test\n    fun comparisonModeComponent_greater_shouldBeSelected() {\n        componentRule.setTestContent {\n            ComparisonModeComponents(\n                modifier = Modifier.fillMaxWidth(),\n                selectedMode = ComparisonMode.GREATER\n            )\n        }\n\n        // Assert that the greater text and icon are displayed and content selected\n\n        componentRule\n            .onNodeWithText(\"Greater\")\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsSelected()\n\n        componentRule\n            .onNodeWithContentDescription(\"Icon of Greater\")\n            .assertExists()\n            .assertIsDisplayed()\n\n        // Assert that the lesser text and icon are displayed and content not selected\n\n        componentRule\n            .onNodeWithText(\"Lesser\")\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsNotSelected()\n\n        componentRule\n            .onNodeWithContentDescription(\"Icon of Lesser\")\n            .assertExists()\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun comparisonModeComponent_lesser_shouldBeSelected() {\n        componentRule.setTestContent {\n            ComparisonModeComponents(\n                modifier = Modifier.fillMaxWidth(),\n                selectedMode = ComparisonMode.LESSER\n            )\n        }\n\n        // Assert that the greater text and icon are displayed and content selected\n\n        componentRule\n            .onNodeWithText(\"Greater\")\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsNotSelected()\n\n        componentRule\n            .onNodeWithContentDescription(\"Icon of Greater\")\n            .assertExists()\n            .assertIsDisplayed()\n\n        // Assert that the lesser text and icon are displayed and content not selected\n\n        componentRule\n            .onNodeWithText(\"Lesser\")\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsSelected()\n\n        componentRule\n            .onNodeWithContentDescription(\"Icon of Lesser\")\n            .assertExists()\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun row_hasCorrectModifiers() {\n        componentRule.setTestContent {\n            ComparisonModeComponents(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .testTag(\"ComparisonModeComponents\"),\n                selectedMode = ComparisonMode.GREATER\n            )\n        }\n\n        componentRule\n            .onNodeWithTag(\"ComparisonModeComponents\")\n            .onChildren()\n            .assertCountEquals(2)\n\n        componentRule\n            .onNodeWithTag(\"ComparisonModeComponents\")\n            .onChildren()\n            .onFirst()\n            .assertTextEquals(\"Greater\")\n            .assertIsDisplayed()\n            .assertIsSelected()\n\n        componentRule\n            .onNodeWithTag(\"ComparisonModeComponents\")\n            .onChildren()\n            .onLast()\n            .assertTextEquals(\"Lesser\")\n            .assertIsDisplayed()\n            .assertIsNotSelected()\n    }\n\n    @Test\n    fun onModeClick_greater_onClick_when_otherModeIsSelected() {\n        var selectedMode by mutableStateOf(ComparisonMode.GREATER)\n\n        componentRule.setTestContent {\n            ComparisonModeComponents(\n                selectedMode = selectedMode,\n                onModeClick = { mode -> selectedMode = mode }\n            )\n        }\n\n        componentRule\n            .onNodeWithText(\"Greater\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertIsSelected()\n\n        // Click on the LESSER mode.\n        componentRule\n            .onNodeWithText(\"Lesser\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertIsNotSelected()\n            .assertHasClickAction()\n            .performClick() // Click on the mode that is not selected.\n            .assertIsSelected() // This should select the mode.\n\n        componentRule\n            .onNodeWithText(\"Greater\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertIsNotSelected()\n\n        // Verify that the onModeClick callback is called with the correct mode.\n        assertThat(selectedMode).isNotNull()\n        assertThat(selectedMode).isEqualTo(ComparisonMode.LESSER)\n    }\n\n    @Test\n    fun onModeClick_greater_onClick_when_sameModeIsSelected() {\n        var selectedMode by mutableStateOf(ComparisonMode.GREATER)\n\n        componentRule.setTestContent {\n            ComparisonModeComponents(\n                selectedMode = selectedMode,\n                onModeClick = { mode -> selectedMode = mode }\n            )\n        }\n\n        componentRule\n            .onNodeWithText(\"Lesser\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertIsNotSelected()\n\n        // Click on the GREATER mode.\n        componentRule\n            .onNodeWithText(\"Greater\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertIsSelected()\n            .assertHasClickAction()\n            .performClick() // Click on the mode that is already selected.\n            .assertIsSelected() // This should not do anything since the mode is already selected.\n\n        componentRule\n            .onNodeWithText(\"Lesser\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertIsNotSelected()\n\n        // Verify that the onModeClick callback is called with the correct mode.\n        assertThat(selectedMode).isNotNull()\n        assertThat(selectedMode).isEqualTo(ComparisonMode.GREATER)\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/androidTest/java/com/infinitepower/newquiz/comparison_quiz/ui/ComparisonQuizScreenTest.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui\n\nimport androidx.activity.ComponentActivity\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.calculateWindowSizeClass\nimport androidx.compose.ui.test.ExperimentalTestApi\nimport androidx.compose.ui.test.assertHasClickAction\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.assertIsEnabled\nimport androidx.compose.ui.test.hasText\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.work.Configuration\nimport androidx.work.WorkManager\nimport androidx.work.testing.SynchronousExecutor\nimport androidx.work.testing.WorkManagerTestInitHelper\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.comparison_quiz.core.ComparisonQuizCoreImpl\nimport com.infinitepower.newquiz.comparison_quiz.data.comparison_quiz.FakeComparisonQuizRepositoryImpl\nimport com.infinitepower.newquiz.core.analytics.LocalDebugAnalyticsHelper\nimport com.infinitepower.newquiz.core.game.ComparisonQuizCore\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizHelperValueState\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.toUiText\nimport com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport io.mockk.every\nimport io.mockk.mockk\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport java.net.URI\nimport javax.inject.Inject\n\n/**\n * Tests for [ComparisonQuizScreen].\n */\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\n@OptIn(\n    ExperimentalMaterial3WindowSizeClassApi::class,\n    ExperimentalTestApi::class\n)\ninternal class ComparisonQuizScreenTest {\n    @get:Rule\n    val hiltRule = HiltAndroidRule(this)\n\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<ComponentActivity>()\n\n    private lateinit var comparisonQuizRepository: ComparisonQuizRepository\n\n    @Inject lateinit var userService: UserService\n\n    private val remoteConfig = mockk<RemoteConfig>(relaxed = true)\n    private val recentCategoriesRepository = mockk<RecentCategoriesRepository>(relaxed = true)\n\n    private lateinit var comparisonQuizCore: ComparisonQuizCore\n\n    private lateinit var viewModel: ComparisonQuizViewModel\n\n    private lateinit var workManager: WorkManager\n\n    private val comparisonMode by lazy { ComparisonMode.GREATER }\n\n    private val category by lazy {\n        ComparisonQuizCategory(\n            id = \"numbers\",\n            name = \"Numbers\".toUiText(),\n            description = \"Numbers description\",\n            image = \"\",\n            questionDescription = ComparisonQuizCategory.QuestionDescription(\n                greater = \"Which number is greater?\",\n                less = \"Which number is lesser?\",\n            ),\n            formatType = NumberFormatType.DEFAULT,\n            dataSourceAttribution = ComparisonQuizCategory.DataSourceAttribution(\n                text = \"NewQuiz API\",\n                logo = \"\"\n            )\n        )\n    }\n\n    private val firstItemHelperValueState = ComparisonQuizHelperValueState.HIDDEN\n\n    @Before\n    fun setUp() {\n        hiltRule.inject()\n\n        comparisonQuizRepository = FakeComparisonQuizRepositoryImpl(\n            initialQuestions = listOf(\n                ComparisonQuizItem(\n                    title = \"Question 1\",\n                    value = 1.0,\n                    imgUri = URI(\"\")\n                ),\n                ComparisonQuizItem(\n                    title = \"Question 2\",\n                    value = 2.0,\n                    imgUri = URI(\"\")\n                ),\n                ComparisonQuizItem(\n                    title = \"Question 3\",\n                    value = 3.0,\n                    imgUri = URI(\"\")\n                ),\n            ),\n            initialCategories = listOf(category)\n        )\n\n        every {\n            remoteConfig.getString(\"comparison_quiz_first_item_helper_value\")\n        } returns firstItemHelperValueState.name\n\n        comparisonQuizCore = ComparisonQuizCoreImpl(\n            comparisonQuizRepository = comparisonQuizRepository,\n            remoteConfig = remoteConfig,\n            analyticsHelper = LocalDebugAnalyticsHelper(),\n            userService = userService\n        )\n\n        val context = InstrumentationRegistry.getInstrumentation().context\n        val config = Configuration.Builder()\n            .setMinimumLoggingLevel(android.util.Log.DEBUG)\n            .setExecutor(SynchronousExecutor())\n            .build()\n\n        // Initialize WorkManager for instrumentation tests.\n        WorkManagerTestInitHelper.initializeTestWorkManager(context, config)\n\n        workManager = WorkManager.getInstance(context)\n\n        viewModel = ComparisonQuizViewModel(\n            comparisonQuizCore = comparisonQuizCore,\n            savedStateHandle = SavedStateHandle(\n                mapOf(\n                    ComparisonQuizScreenNavArg::comparisonMode.name to comparisonMode,\n                    ComparisonQuizScreenNavArg::category.name to category.toEntity()\n                )\n            ),\n            comparisonQuizRepository = comparisonQuizRepository,\n            workManager = workManager,\n            recentCategoriesRepository = recentCategoriesRepository,\n            userService = userService\n        )\n\n        composeTestRule.setContent {\n            val windowSizeClass = calculateWindowSizeClass(activity = composeTestRule.activity)\n\n            com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme {\n                com.infinitepower.newquiz.core.testing.utils.setTestDeviceLocale()\n\n                ComparisonQuizScreen(\n                    windowSizeClass = windowSizeClass,\n                    navigator = EmptyDestinationsNavigator,\n                    viewModel = viewModel\n                )\n            }\n        }\n    }\n\n    @Test\n    fun comparisonQuizScreen_testComponentsDisplayed() {\n        val questionDescription = category.getQuestionDescription(comparisonMode)\n\n        composeTestRule.waitUntilAtLeastOneExists(\n            matcher = hasText(questionDescription),\n            timeoutMillis = 5000\n        )\n\n        // Check if the question description is displayed\n        composeTestRule\n            .onNodeWithText(questionDescription)\n            .assertIsDisplayed()\n\n        // Check if the first item is displayed\n        composeTestRule\n            .onNodeWithText(\"Question 1\")\n            .assertIsDisplayed()\n\n        // Check if first item helper text is displayed\n        composeTestRule\n            .onNodeWithText(\"1\")\n            .apply {\n                if (firstItemHelperValueState == ComparisonQuizHelperValueState.HIDDEN) {\n                    assertDoesNotExist()\n                } else {\n                    assertIsDisplayed()\n                }\n            }\n\n        // Check if the second item is displayed\n        composeTestRule\n            .onNodeWithText(\"Question 2\")\n            .assertIsDisplayed()\n\n        // Check if second item helper text is not displayed\n        composeTestRule\n            .onNodeWithText(\"2\")\n            .assertDoesNotExist()\n\n        // Check if data source attribution is displayed\n        composeTestRule\n            .onNodeWithText(\"NewQuiz API\")\n            .assertIsDisplayed()\n\n        // Check if current position is displayed\n        composeTestRule\n            .onNodeWithText(\"Position: 1\")\n            .assertIsDisplayed()\n\n        // Check if highest position is displayed\n        composeTestRule\n            .onNodeWithText(\"Highest: 1\")\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun comparisonQuizScreen_testWrongAnswerClick() {\n        // Click on the first item (wrong answer)\n        // The value of the first item is 1.0\n        // The value of the second item is 2.0\n        composeTestRule\n            .onNodeWithText(\"Question 1\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertHasClickAction()\n            .performClick()\n            // When the wrong answer is clicked should end the game\n            .assertDoesNotExist()\n\n        // Check if the game is over\n        assertThat(viewModel.uiState.value.isGameOver).isTrue()\n\n        // Check if the game over screen is displayed\n        composeTestRule\n            .onNodeWithText(\"Game Over\")\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun comparisonQuizScreen_testCorrectAnswerClick() {\n        // Click on the second item (correct answer)\n        // The value of the first item is 1.0\n        // The value of the second item is 2.0\n        composeTestRule\n            .onNodeWithText(\"Question 2\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertHasClickAction()\n            .performClick()\n            // When the correct answer is clicked should not end the game\n            .assertIsDisplayed()\n            .assertHasClickAction()\n\n        // Check if the game is not over\n        assertThat(viewModel.uiState.value.isGameOver).isFalse()\n\n        // Check if the game over screen is not displayed\n        composeTestRule\n            .onNodeWithText(\"Game Over\")\n            .assertDoesNotExist()\n\n        // Now the first item should be the second item\n        // and the second item should be a new item\n        composeTestRule\n            .onNodeWithText(\"Question 3\")\n            .assertIsDisplayed()\n            .assertHasClickAction()\n    }\n}"
  },
  {
    "path": "comparison-quiz/src/androidTest/java/com/infinitepower/newquiz/comparison_quiz/ui/components/ComparisonItemTest.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui.components\n\nimport android.net.Uri\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertHasClickAction\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.assertIsEnabled\nimport androidx.compose.ui.test.hasText\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizHelperValueState\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class ComparisonItemTest {\n    @get:Rule\n    val componentRule = createComposeRule()\n\n    @Test\n    fun comparisonItem_titleAndHelperValueDisplayed() {\n        val title = \"Item title\"\n        val helperValue = \"Helper value\"\n        val helperValueState = ComparisonQuizHelperValueState.NORMAL\n        var onClickCalled = false\n\n        componentRule.setContent {\n            ComparisonItem(\n                title = title,\n                image = Uri.EMPTY,\n                helperValue = helperValue,\n                helperContentAlignment = Alignment.BottomEnd,\n                helperValueState = helperValueState,\n                onClick = { onClickCalled = true },\n                modifier = Modifier.testTag(\"ComparisonItem\")\n            )\n        }\n\n        componentRule\n            .onNode(hasText(title) or hasText(helperValue))\n            .assertExists()\n            .assertIsDisplayed()\n\n        componentRule\n            .onNodeWithTag(\"ComparisonItem\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertHasClickAction()\n            // Click on the item\n            .performClick()\n            // Check that the item is still displayed and enabled after the click\n            .assertIsDisplayed()\n            .assertIsEnabled()\n\n        assertThat(onClickCalled).isTrue()\n    }\n\n    @Test\n    fun comparisonItem_titleDisplayed_andHelperValueNotDisplayedWhenStateHidden() {\n        val title = \"Item title\"\n        val helperValue = \"Helper value\"\n        val helperValueState = ComparisonQuizHelperValueState.HIDDEN\n        var onClickCalled = false\n\n        componentRule.setContent {\n            ComparisonItem(\n                title = title,\n                image = Uri.EMPTY,\n                helperValue = helperValue,\n                helperContentAlignment = Alignment.BottomEnd,\n                helperValueState = helperValueState,\n                onClick = { onClickCalled = true },\n                modifier = Modifier.testTag(\"ComparisonItem\")\n            )\n        }\n\n        componentRule\n            .onNodeWithText(title)\n            .assertExists()\n            .assertIsDisplayed()\n\n        componentRule\n            .onNodeWithText(helperValue)\n            .assertDoesNotExist()\n\n        componentRule\n            .onNodeWithTag(\"ComparisonItem\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertHasClickAction()\n            // Click on the item\n            .performClick()\n            // Check that the item is still displayed and enabled after the click\n            .assertIsDisplayed()\n            .assertIsEnabled()\n\n        assertThat(onClickCalled).isTrue()\n    }\n}"
  },
  {
    "path": "comparison-quiz/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n</manifest>"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/core/ComparisonQuizCoreImpl.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.core\n\nimport android.util.Log\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.game.ComparisonQuizCore\nimport com.infinitepower.newquiz.core.game.ComparisonQuizCore.InitializationData\nimport com.infinitepower.newquiz.core.game.ComparisonQuizCore.QuizData\nimport com.infinitepower.newquiz.core.game.GameOverException\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.user_services.InsufficientDiamondsException\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.data.util.mappers.comparisonquiz.toModel\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.update\nimport javax.inject.Inject\n\nprivate const val TAG = \"ComparisonQuizCoreImpl\"\n\n/**\n * Represents the implementation of the [ComparisonQuizCore] interface.\n *\n * @param comparisonQuizRepository The repository for retrieving comparison quiz data.\n */\nclass ComparisonQuizCoreImpl @Inject constructor(\n    private val comparisonQuizRepository: ComparisonQuizRepository,\n    private val remoteConfig: RemoteConfig,\n    private val analyticsHelper: AnalyticsHelper,\n    private val userService: UserService\n) : ComparisonQuizCore {\n    private val _quizData = MutableStateFlow(QuizData())\n    override val quizDataFlow = _quizData.asStateFlow()\n\n    override suspend fun initializeGame(initializationData: InitializationData) {\n        val category = initializationData.category\n        val questions = initializationData.initialItems\n            .map(ComparisonQuizItemEntity::toModel)\n            .ifEmpty {\n                // If there are no initial items, get the questions from the repository\n                comparisonQuizRepository.getQuestions(initializationData.category)\n            }\n\n        if (questions.isEmpty()) {\n            Log.w(TAG, \"No questions found for category ${initializationData.category.id}\")\n            return endGame()\n        }\n\n        val comparisonMode = initializationData.comparisonMode\n        val questionDescription = category.getQuestionDescription(comparisonMode)\n\n        val firstItemHelperValue =\n            remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_FIRST_ITEM_HELPER_VALUE)\n\n        val quizData = QuizData(\n            category = category,\n            questions = questions,\n            comparisonMode = comparisonMode,\n            questionDescription = questionDescription,\n            firstItemHelperValueState = firstItemHelperValue,\n        )\n\n        analyticsHelper.logEvent(\n            AnalyticsEvent.ComparisonQuizGameStart(\n                category = category.id,\n                comparisonMode = comparisonMode.name,\n            )\n        )\n\n        _quizData.emit(quizData)\n        startGame()\n    }\n\n    override fun startGame() {\n        _quizData.update { currentData ->\n            try {\n                currentData.getNextQuestion()\n            } catch (e: GameOverException) {\n                e.printStackTrace()\n                return endGame()\n            }\n        }\n    }\n\n    override fun onAnswerClicked(answer: ComparisonQuizItem) {\n        Log.d(TAG, \"Answer clicked: $answer\")\n\n        _quizData.update { currentData ->\n            val currentQuestion = currentData.currentQuestion\n            val isQuestionCorrect = currentQuestion != null && currentQuestion.isCorrectAnswer(answer)\n            Log.d(TAG, \"Is question correct: $isQuestionCorrect\")\n\n            // If the current question is null or the answer is correct, get the next question\n            if (currentQuestion == null || isQuestionCorrect) {\n                try {\n                    currentData.getNextQuestion()\n                } catch (e: GameOverException) {\n                    e.printStackTrace()\n                    currentData.copy(\n                        currentQuestion = null,\n                        isGameOver = true,\n                        isLastQuestionCorrect = isQuestionCorrect\n                    )\n                }\n            } else {\n                currentData.copy(\n                    currentQuestion = null,\n                    isGameOver = true,\n                    isLastQuestionCorrect = false\n                )\n            }\n        }\n    }\n\n    override fun endGame() {\n        _quizData.update { currentData ->\n            currentData.copy(\n                currentQuestion = null,\n                isGameOver = true\n            )\n        }\n    }\n\n    override val skipCost: UInt\n        get() = remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_SKIP_COST).toUInt()\n\n    override suspend fun getUserDiamonds(): UInt = userService.getUserDiamonds()\n\n    override suspend fun skip() {\n        Log.d(TAG, \"Skipping question\")\n\n        val userDiamonds = getUserDiamonds()\n        val skipCost = skipCost\n\n        // Check if the user has enough diamonds to skip the question\n        if (userDiamonds < skipCost) {\n            throw InsufficientDiamondsException(\n                diamondsNeeded = skipCost,\n                diamondsAvailable = userDiamonds\n            )\n        }\n\n        // Update the user's diamond count\n        userService.addRemoveDiamonds(-skipCost.toInt())\n\n        // Update the quiz data to the next question\n        _quizData.update { currentData ->\n            try {\n                currentData.getNextQuestion(skipped = true)\n            } catch (e: GameOverException) {\n                e.printStackTrace()\n                return endGame()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/core/workers/ComparisonQuizEndGameWorker.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.core.workers\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.OneTimeWorkRequestBuilder\nimport androidx.work.WorkManager\nimport androidx.work.WorkerParameters\nimport androidx.work.workDataOf\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\n\n@HiltWorker\nclass ComparisonQuizEndGameWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    private val analyticsHelper: AnalyticsHelper,\n    private val userService: UserService,\n) : CoroutineWorker(appContext, workerParams) {\n    companion object {\n        private const val CATEGORY_ID = \"category_id\"\n        private const val COMPARISON_MODE_NAME = \"comparison_mode_name\"\n        private const val END_POSITION = \"end_position\"\n        private const val SKIPPED_ANSWERS = \"skipped_answers\"\n\n        fun enqueueWork(\n            workManager: WorkManager,\n            categoryId: String,\n            comparisonMode: ComparisonMode,\n            endPosition: Int,\n            skippedAnswers: Int\n        ) {\n            val data = workDataOf(\n                CATEGORY_ID to categoryId,\n                COMPARISON_MODE_NAME to comparisonMode.name,\n                END_POSITION to endPosition,\n                SKIPPED_ANSWERS to skippedAnswers\n            )\n\n            val workRequest = OneTimeWorkRequestBuilder<ComparisonQuizEndGameWorker>()\n                .setInputData(data)\n                .build()\n\n            workManager.enqueue(workRequest)\n        }\n    }\n\n    override suspend fun doWork(): Result {\n        val categoryId = inputData.getString(CATEGORY_ID) ?: return Result.failure()\n        val comparisonModeName = inputData.getString(COMPARISON_MODE_NAME) ?: return Result.failure()\n        val endPosition = inputData.getInt(END_POSITION, 0)\n        val skippedAnswers = inputData.getInt(SKIPPED_ANSWERS, 0)\n\n        analyticsHelper.logEvent(\n            AnalyticsEvent.ComparisonQuizGameEnd(\n                category = categoryId,\n                comparisonMode = comparisonModeName,\n                score = endPosition,\n            )\n        )\n\n        userService.saveComparisonQuizGame(\n            categoryId = categoryId,\n            comparisonMode = comparisonModeName,\n            endPosition = endPosition.toUInt(),\n            skippedAnswers = skippedAnswers.toUInt(),\n            generateXp = true\n        )\n\n        return Result.success()\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/di/ComparisonQuizModule.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.di\n\nimport com.infinitepower.newquiz.comparison_quiz.core.ComparisonQuizCoreImpl\nimport com.infinitepower.newquiz.core.game.ComparisonQuizCore\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.components.ViewModelComponent\n\n@Module\n@InstallIn(ViewModelComponent::class)\nabstract class ComparisonQuizModule {\n    @Binds\n    abstract fun bindComparisonQuizCore(impl: ComparisonQuizCoreImpl): ComparisonQuizCore\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/list/ComparisonQuizListScreen.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.list\n\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.comparison_quiz.destinations.ComparisonQuizScreenDestination\nimport com.infinitepower.newquiz.comparison_quiz.list.components.ComparisonModeComponents\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.LocalAnalyticsHelper\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.home.HomeLazyColumn\nimport com.infinitepower.newquiz.core.ui.home.homeCategoriesItems\nimport com.infinitepower.newquiz.domain.repository.home.HomeCategories\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.toUiText\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.collections.immutable.persistentListOf\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@Destination\nfun ComparisonQuizListScreen(\n    navigator: DestinationsNavigator,\n    viewModel: ComparisonQuizListScreenViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    val analyticsHelper = LocalAnalyticsHelper.current\n\n    ComparisonQuizListScreenImpl(\n        uiState = uiState,\n        onCategoryClick = { category ->\n            analyticsHelper.logEvent(\n                AnalyticsEvent.CategoryClicked(\n                    game = AnalyticsEvent.Game.COMPARISON_QUIZ,\n                    categoryId = category.id,\n                    otherData = mapOf(\n                        \"comparison_mode\" to uiState.selectedMode.name.lowercase(),\n                    )\n                )\n            )\n\n            navigator.navigate(\n                ComparisonQuizScreenDestination(\n                    categoryId = category.id,\n                    comparisonMode = uiState.selectedMode\n                )\n            )\n        },\n        onSelectMode = { mode ->\n            viewModel.onEvent(ComparisonQuizListScreenUiEvent.SelectMode(mode))\n        }\n    )\n}\n\n@Composable\nprivate fun ComparisonQuizListScreenImpl(\n    uiState: ComparisonQuizListScreenUiState,\n    onCategoryClick: (category: ComparisonQuizCategory) -> Unit,\n    onSelectMode: (mode: ComparisonMode) -> Unit\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    var seeAllCategories by remember { mutableStateOf(false) }\n\n    HomeLazyColumn {\n        item {\n            Column {\n                Text(\n                    text = stringResource(id = CoreR.string.select_a_comparison_mode_for_the_first_item),\n                    style = MaterialTheme.typography.bodyMedium\n                )\n                Spacer(modifier = Modifier.height(spaceMedium))\n                ComparisonModeComponents(\n                    modifier = Modifier.fillParentMaxWidth(),\n                    onModeClick = onSelectMode,\n                    selectedMode = uiState.selectedMode\n                )\n            }\n        }\n\n        item {\n            Text(\n                text = stringResource(id = CoreR.string.categories),\n                style = MaterialTheme.typography.bodyMedium\n            )\n        }\n\n        homeCategoriesItems(\n            seeAllCategories = seeAllCategories,\n            recentCategories = uiState.homeCategories.recentCategories,\n            otherCategories = uiState.homeCategories.otherCategories,\n            isInternetAvailable = uiState.internetConnectionAvailable,\n            onCategoryClick = onCategoryClick,\n            onSeeAllCategoriesClick = { seeAllCategories = !seeAllCategories },\n            showConnectionInfo = uiState.showCategoryConnectionInfo\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun ComparisonQuizListScreenPreview() {\n    NewQuizTheme {\n        Surface {\n            ComparisonQuizListScreenImpl(\n                uiState = ComparisonQuizListScreenUiState(\n                    homeCategories = HomeCategories(\n                        recentCategories = persistentListOf(\n                            ComparisonQuizCategory(\n                                id = \"test\",\n                                description = \"Description\",\n                                name = \"Title\".toUiText(),\n                                image = \"\",\n                                questionDescription = ComparisonQuizCategory.QuestionDescription(\n                                    greater = \"Greater\",\n                                    less = \"Less\"\n                                ),\n                                formatType = NumberFormatType.DEFAULT\n                            )\n                        ),\n                        otherCategories = persistentListOf()\n                    )\n                ),\n                onCategoryClick = {},\n                onSelectMode = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/list/ComparisonQuizListScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.list\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\n\ninterface ComparisonQuizListScreenUiEvent {\n    @Keep\n    data class SelectMode(\n        val mode: ComparisonMode\n    ) : ComparisonQuizListScreenUiEvent\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/list/ComparisonQuizListScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.list\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.domain.repository.home.HomeCategories\nimport com.infinitepower.newquiz.domain.repository.home.emptyHomeCategories\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\n\n@Keep\ndata class ComparisonQuizListScreenUiState(\n    val homeCategories: HomeCategories<ComparisonQuizCategory> = emptyHomeCategories(),\n    val selectedMode: ComparisonMode = ComparisonMode.GREATER,\n    val internetConnectionAvailable: Boolean = true,\n    val showCategoryConnectionInfo: ShowCategoryConnectionInfo = ShowCategoryConnectionInfo.NONE\n)\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/list/ComparisonQuizListScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.list\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.core.network.NetworkStatusTracker\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.stateIn\nimport kotlinx.coroutines.flow.update\nimport javax.inject.Inject\n\n@HiltViewModel\nclass ComparisonQuizListScreenViewModel @Inject constructor(\n    recentCategoriesRepository: RecentCategoriesRepository,\n    networkStatusTracker: NetworkStatusTracker\n) : ViewModel() {\n    private val _uiState = MutableStateFlow(ComparisonQuizListScreenUiState())\n\n    val uiState = combine(\n        _uiState,\n        recentCategoriesRepository.getComparisonCategories(\n            isInternetAvailable = networkStatusTracker.isCurrentlyConnected()\n        ),\n        recentCategoriesRepository.getShowCategoryConnectionInfoFlow()\n    ) { uiState, recentCategories, showCategoryConnectionInfo ->\n        uiState.copy(\n            homeCategories = recentCategories,\n            internetConnectionAvailable = networkStatusTracker.isCurrentlyConnected(),\n            showCategoryConnectionInfo = showCategoryConnectionInfo\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(),\n        initialValue = ComparisonQuizListScreenUiState()\n    )\n\n    fun onEvent(event: ComparisonQuizListScreenUiEvent) {\n        when (event) {\n            is ComparisonQuizListScreenUiEvent.SelectMode -> {\n                _uiState.update { currentState ->\n                    currentState.copy(selectedMode = event.mode)\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/list/components/ComparisonModeComponent.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.list.components\n\nimport androidx.compose.animation.animateColor\nimport androidx.compose.animation.core.Transition\nimport androidx.compose.animation.core.updateTransition\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.ChevronLeft\nimport androidx.compose.material.icons.rounded.ChevronRight\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.State\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Shape\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\n\n@Composable\ninternal fun ComparisonModeComponent(\n    modifier: Modifier = Modifier,\n    selected: Boolean,\n    enabled: Boolean = true,\n    mode: ComparisonMode,\n    shape: Shape = ComparisonModeDefaults.shape,\n    colors: ComparisonModeColors = ComparisonModeDefaults.defaultColors(),\n    onClick: () -> Unit = {}\n) {\n    val title = when (mode) {\n        ComparisonMode.GREATER -> stringResource(id = R.string.greater)\n        ComparisonMode.LESSER -> stringResource(id = R.string.lesser)\n    }\n\n    val icon = when (mode) {\n        ComparisonMode.GREATER -> Icons.Rounded.ChevronRight\n        ComparisonMode.LESSER -> Icons.Rounded.ChevronLeft\n    }\n\n    val transition = updateTransition(targetState = selected, label = \"$title Mode\")\n\n    val containerColor = colors.containerColor(transition).value\n    val contentColor = colors.contentColor(transition).value\n    val iconColor = colors.iconColor(transition).value\n    val iconContainerColor = colors.iconContainerColor(transition).value\n\n    val border = if (selected) null else BorderStroke(1.dp, MaterialTheme.colorScheme.outline)\n\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    Surface(\n        modifier = modifier,\n        onClick = onClick,\n        shape = shape,\n        color = containerColor,\n        contentColor = contentColor,\n        border = border,\n        selected = selected,\n        enabled = enabled\n    ) {\n        Column(\n            modifier = Modifier.padding(spaceMedium)\n        ) {\n            Text(\n                text = title,\n                style = MaterialTheme.typography.titleMedium\n            )\n            Spacer(modifier = Modifier.height(spaceMedium))\n            Box(\n                modifier = Modifier.fillMaxWidth(),\n                contentAlignment = Alignment.CenterEnd\n            ) {\n                Surface(\n                    shape = CircleShape,\n                    color = iconContainerColor,\n                    contentColor = iconColor\n                ) {\n                    Icon(\n                        imageVector = icon,\n                        contentDescription = stringResource(id = R.string.icon_of_s, title),\n                        modifier = Modifier.padding(MaterialTheme.spacing.extraSmall)\n                    )\n                }\n            }\n        }\n    }\n}\n\nobject ComparisonModeDefaults {\n    val shape: Shape @Composable get() = MaterialTheme.shapes.large\n\n    @Composable\n    fun defaultColors(\n        containerColor: Color = MaterialTheme.colorScheme.surface,\n        selectedContainerColor: Color = MaterialTheme.colorScheme.primary,\n        contentColor: Color = MaterialTheme.colorScheme.onSurface,\n        selectedContentColor: Color = MaterialTheme.colorScheme.onPrimary,\n        iconColor: Color = MaterialTheme.colorScheme.onSurface,\n        selectedIconColor: Color = MaterialTheme.colorScheme.primary,\n        iconContainerColor: Color = MaterialTheme.colorScheme.surfaceVariant,\n        selectedIconContainerColor: Color = MaterialTheme.colorScheme.onPrimary\n    ): ComparisonModeColors = ComparisonModeColors(\n        containerColor = containerColor,\n        selectedContainerColor = selectedContainerColor,\n        contentColor = contentColor,\n        selectedContentColor = selectedContentColor,\n        iconColor = iconColor,\n        selectedIconColor = selectedIconColor,\n        iconContainerColor = iconContainerColor,\n        selectedIconContainerColor = selectedIconContainerColor\n    )\n}\n\n@Immutable\nclass ComparisonModeColors internal constructor(\n    private val containerColor: Color,\n    private val selectedContainerColor: Color,\n    private val contentColor: Color,\n    private val selectedContentColor: Color,\n    private val iconColor: Color,\n    private val selectedIconColor: Color,\n    private val iconContainerColor: Color,\n    private val selectedIconContainerColor: Color\n) {\n    @Composable\n    internal fun containerColor(\n        transition: Transition<Boolean>\n    ): State<Color> {\n        return transition.animateColor(\n            label = \"Container Color\"\n        ) { selected ->\n            if (selected) selectedContainerColor else containerColor\n        }\n    }\n\n    @Composable\n    internal fun contentColor(\n        transition: Transition<Boolean>\n    ): State<Color> {\n        return transition.animateColor(\n            label = \"Content Color\"\n        ) { selected ->\n            if (selected) selectedContentColor else contentColor\n        }\n    }\n\n    @Composable\n    internal fun iconColor(\n        transition: Transition<Boolean>\n    ): State<Color> {\n        return transition.animateColor(\n            label = \"Icon Color\"\n        ) { selected ->\n            if (selected) selectedIconColor else iconColor\n        }\n    }\n\n    @Composable\n    internal fun iconContainerColor(\n        transition: Transition<Boolean>\n    ): State<Color> {\n        return transition.animateColor(\n            label = \"Icon Container Color\"\n        ) { selected ->\n            if (selected) selectedIconContainerColor else iconContainerColor\n        }\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (other !is ComparisonModeColors) return false\n\n        if (containerColor != other.containerColor) return false\n        if (selectedContainerColor != other.selectedContainerColor) return false\n        if (contentColor != other.contentColor) return false\n        if (selectedContentColor != other.selectedContentColor) return false\n        if (iconColor != other.iconColor) return false\n        if (selectedIconColor != other.selectedIconColor) return false\n        if (iconContainerColor != other.iconContainerColor) return false\n        if (selectedIconContainerColor != other.selectedIconContainerColor) return false\n\n        return true\n    }\n\n    override fun hashCode(): Int {\n        var result = containerColor.hashCode()\n        result = 31 * result + selectedContainerColor.hashCode()\n        result = 31 * result + contentColor.hashCode()\n        result = 31 * result + selectedContentColor.hashCode()\n        result = 31 * result + iconColor.hashCode()\n        result = 31 * result + selectedIconColor.hashCode()\n        result = 31 * result + iconContainerColor.hashCode()\n        result = 31 * result + selectedIconContainerColor.hashCode()\n        return result\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun ComparisonModeComponentPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) selected: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            ComparisonModeComponent(\n                modifier = Modifier\n                    .padding(16.dp)\n                    .width(120.dp),\n                mode = ComparisonMode.GREATER,\n                onClick = {},\n                selected = selected\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/list/components/ComparisonModeComponents.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.list.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\n\n@Composable\ninternal fun ComparisonModeComponents(\n    modifier: Modifier = Modifier,\n    selectedMode: ComparisonMode = ComparisonMode.GREATER,\n    onModeClick: (mode: ComparisonMode) -> Unit = {}\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    Row(\n        modifier = modifier,\n        horizontalArrangement = Arrangement.spacedBy(spaceMedium)\n    ) {\n        ComparisonModeComponent(\n            mode = ComparisonMode.GREATER,\n            modifier = Modifier.weight(1f),\n            selected = selectedMode == ComparisonMode.GREATER,\n            onClick = { onModeClick(ComparisonMode.GREATER) }\n        )\n        ComparisonModeComponent(\n            mode = ComparisonMode.LESSER,\n            modifier = Modifier.weight(1f),\n            selected = selectedMode == ComparisonMode.LESSER,\n            onClick = { onModeClick(ComparisonMode.LESSER) }\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun ComparisonModeComponentsPreview() {\n    NewQuizTheme {\n        Surface {\n            ComparisonModeComponents(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .padding(16.dp)\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/AnimationState.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui\n\n/**\n * Represents the state of the animation in the comparison quiz game.\n */\ninternal enum class AnimationState {\n    /**\n     * This is the state before the game starts.\n     * Animates the items into the screen.\n     *\n     * Next [AnimationState]: [Normal]\n     */\n    StartGame,\n\n    /**\n     * This is the state for when no animation is running.\n     *\n     * Next [AnimationState]: [NextQuestion]\n     */\n    Normal,\n\n    /**\n     * This animation state runs when the user selects the correct answer\n     * and the question is transitioning to the next question.\n     *\n     * Next [AnimationState]: [Normal]\n     */\n    NextQuestion;\n\n    /**\n     * Returns the next [AnimationState] in the sequence.\n     */\n    fun next(): AnimationState {\n        return when (this) {\n            StartGame -> Normal\n            Normal -> NextQuestion\n            NextQuestion -> Normal\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/ComparisonQuizScreen.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui\n\nimport androidx.annotation.Keep\nimport androidx.compose.animation.AnimatedVisibility\nimport androidx.compose.animation.ExperimentalAnimationApi\nimport androidx.compose.animation.core.updateTransition\nimport androidx.compose.animation.fadeIn\nimport androidx.compose.animation.fadeOut\nimport androidx.compose.animation.slideInHorizontally\nimport androidx.compose.animation.slideInVertically\nimport androidx.compose.animation.slideOutHorizontally\nimport androidx.compose.animation.slideOutVertically\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.WindowHeightSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowWidthSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.movableContentOf\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.platform.LocalConfiguration\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.tooling.preview.PreviewScreenSizes\nimport androidx.compose.ui.unit.DpSize\nimport androidx.compose.ui.unit.dp\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport androidx.navigation.NavController\nimport coil.compose.AsyncImage\nimport com.infinitepower.newquiz.comparison_quiz.destinations.ComparisonQuizScreenDestination\nimport com.infinitepower.newquiz.comparison_quiz.ui.components.ComparisonItem\nimport com.infinitepower.newquiz.comparison_quiz.ui.components.ComparisonMidContent\nimport com.infinitepower.newquiz.comparison_quiz.ui.components.GameOverContent\nimport com.infinitepower.newquiz.core.NumberFormatter\nimport com.infinitepower.newquiz.core.navigation.MazeNavigator\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.core.ui.components.skip_question.SkipIconButton\nimport com.infinitepower.newquiz.core.ui.components.skip_question.SkipQuestionDialog\nimport com.infinitepower.newquiz.core.util.emptyJavaURI\nimport com.infinitepower.newquiz.core.util.plus\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizHelperValueState\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\nimport com.infinitepower.newquiz.model.regional_preferences.RegionalPreferences\nimport com.infinitepower.newquiz.model.toUiText\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.coroutines.delay\n\n@Composable\n@Destination(navArgsDelegate = ComparisonQuizScreenNavArg::class)\n@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)\nfun ComparisonQuizScreen(\n    windowSizeClass: WindowSizeClass,\n    navigator: DestinationsNavigator,\n    mazeNavigator: MazeNavigator,\n    navController: NavController,\n    viewModel: ComparisonQuizViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    var animationState by remember { mutableStateOf(AnimationState.StartGame) }\n\n    LaunchedEffect(key1 = uiState.currentPosition) {\n        if (uiState.currentPosition == 1) {\n            animationState = AnimationState.Normal\n        } else if (uiState.currentPosition > 1) {\n            animationState = AnimationState.NextQuestion\n            delay(ANIMATIONS_DELAY)\n            animationState = AnimationState.Normal\n        }\n    }\n\n    val mazeItemId = remember(navController) {\n        val backStackEntry = navController.getBackStackEntry(ComparisonQuizScreenDestination.route)\n        val args = ComparisonQuizScreenDestination.argsFrom(backStackEntry)\n        args.mazeItemId\n    }\n\n    ComparisonQuizScreenImpl(\n        uiState = uiState,\n        windowSizeClass = windowSizeClass,\n        animationState = animationState,\n        isFromMaze = mazeItemId != null,\n        onEvent = viewModel::onEvent,\n        onBackClick = navigator::popBackStack,\n        onPlayAgainClick = {\n            val category = uiState.gameCategory ?: return@ComparisonQuizScreenImpl\n            val comparisonMode = uiState.comparisonMode ?: return@ComparisonQuizScreenImpl\n\n            navigator.navigate(\n                ComparisonQuizScreenDestination(\n                    categoryId = category.id,\n                    comparisonMode = comparisonMode\n                )\n            ) {\n                popUpTo(ComparisonQuizScreenDestination) {\n                    inclusive = true\n                }\n            }\n        }\n    )\n\n    // If the game is over and is from maze, navigate to maze results\n    LaunchedEffect(uiState.isGameOver) {\n        if (uiState.isGameOver) {\n            delay(NAV_TO_RESULTS_DELAY_MILLIS)\n\n            if (uiState.gameCategory == null) {\n                navigator.popBackStack()\n            } else if (mazeItemId != null) {\n                mazeNavigator.navigateToMazeResults(mazeItemId)\n            }\n        }\n    }\n}\n\n@Composable\n@ExperimentalMaterial3Api\n@ExperimentalAnimationApi\ninternal fun ComparisonQuizScreenImpl(\n    uiState: ComparisonQuizUiState,\n    windowSizeClass: WindowSizeClass,\n    animationState: AnimationState,\n    isFromMaze: Boolean = false,\n    onBackClick: () -> Unit = {},\n    onPlayAgainClick: () -> Unit = {},\n    onEvent: (event: ComparisonQuizUiEvent) -> Unit = {}\n) {\n    val verticalContent = remember(windowSizeClass) {\n        windowSizeClass.heightSizeClass > WindowHeightSizeClass.Compact &&\n                windowSizeClass.widthSizeClass < WindowWidthSizeClass.Expanded\n    }\n\n    when {\n        uiState.currentQuestion != null && uiState.gameDescription != null && uiState.gameCategory != null -> {\n            ComparisonQuizContent(\n                modifier = Modifier.fillMaxSize(),\n                currentQuestion = uiState.currentQuestion,\n                gameDescription = uiState.gameDescription,\n                questionPosition = uiState.currentPosition,\n                highestPosition = uiState.highestPosition,\n                verticalContent = verticalContent,\n                onBackClick = onBackClick,\n                gameCategory = uiState.gameCategory,\n                userAvailable = uiState.userAvailable,\n                firstItemHelperValueState = uiState.firstItemHelperValueState,\n                animationState = animationState,\n                regionalPreferences = uiState.regionalPreferences,\n                onAnswerClick = { onEvent(ComparisonQuizUiEvent.OnAnswerClick(it)) },\n                onSkipClick = { onEvent(ComparisonQuizUiEvent.ShowSkipQuestionDialog) },\n            )\n        }\n\n        uiState.isGameOver && uiState.gameCategory != null && !isFromMaze -> {\n            GameOverContent(\n                scorePosition = uiState.currentPosition,\n                highestPosition = uiState.highestPosition,\n                onBackClick = onBackClick,\n                onPlayAgainClick = onPlayAgainClick\n            )\n        }\n\n        else -> {\n            Box(\n                modifier = Modifier.fillMaxSize(),\n                contentAlignment = Alignment.Center\n            ) {\n                CircularProgressIndicator()\n            }\n        }\n    }\n\n    SkipQuestionDialog(\n        userDiamonds = uiState.userDiamonds,\n        skipCost = uiState.skipCost.toInt(),\n        loading = uiState.userDiamondsLoading,\n        onSkipClick = { onEvent(ComparisonQuizUiEvent.SkipQuestion) },\n        onDismissClick = { onEvent(ComparisonQuizUiEvent.DismissSkipQuestionDialog) }\n    )\n}\n\n@Keep\ndata class ComparisonQuizScreenNavArg(\n    val categoryId: String,\n    val comparisonMode: ComparisonMode = ComparisonMode.GREATER,\n    val initialItems: ArrayList<ComparisonQuizItemEntity> = arrayListOf(),\n    val mazeItemId: Int? = null\n)\n\n@Composable\n@ExperimentalAnimationApi\nprivate fun ComparisonQuizContent(\n    modifier: Modifier = Modifier,\n    onBackClick: () -> Unit,\n    onSkipClick: () -> Unit,\n    onAnswerClick: (ComparisonQuizItem) -> Unit,\n    currentQuestion: ComparisonQuizQuestion,\n    gameCategory: ComparisonQuizCategory,\n    gameDescription: String,\n    questionPosition: Int,\n    highestPosition: Int,\n    verticalContent: Boolean,\n    userAvailable: Boolean,\n    firstItemHelperValueState: ComparisonQuizHelperValueState,\n    animationState: AnimationState,\n    regionalPreferences: RegionalPreferences = RegionalPreferences()\n) {\n    val valueFormatter = remember(gameCategory) {\n        NumberFormatter.from(gameCategory.formatType)\n    }\n\n    ComparisonQuizContainer(\n        modifier = modifier.fillMaxSize(),\n        verticalContent = verticalContent,\n        animationState = animationState,\n        descriptionContent = {\n            Text(\n                text = gameDescription,\n                style = MaterialTheme.typography.bodyLarge,\n                maxLines = 2,\n                overflow = TextOverflow.Ellipsis\n            )\n        },\n        firstQuestionContent = {\n            val item = currentQuestion.questions.first\n\n            val helperValue = remember(item, gameCategory, regionalPreferences, valueFormatter) {\n                valueFormatter.formatValueToString(\n                    value = item.value,\n                    helperValueSuffix = gameCategory.helperValueSuffix,\n                    regionalPreferences = regionalPreferences\n                )\n            }\n\n            ComparisonItem(\n                item = item,\n                helperValue = helperValue,\n                onClick = { onAnswerClick(currentQuestion.questions.first) },\n                helperContentAlignment = Alignment.BottomCenter,\n                helperValueState = firstItemHelperValueState,\n            )\n        },\n        secondQuestionContent = {\n            ComparisonItem(\n                item = currentQuestion.questions.second,\n                helperValue = \"\", // No helper value for the second question\n                onClick = { onAnswerClick(currentQuestion.questions.second) },\n                helperContentAlignment = if (verticalContent) Alignment.TopCenter else Alignment.BottomCenter,\n                helperValueState = ComparisonQuizHelperValueState.HIDDEN,\n            )\n        },\n        midContent = {\n            ComparisonMidContent(\n                questionPosition = questionPosition,\n                highestPosition = highestPosition,\n                verticalContent = verticalContent,\n                animationState = animationState\n            )\n        },\n        backIconContent = { BackIconButton(onClick = onBackClick) },\n        skipButtonContent = {\n            if (userAvailable) {\n                SkipIconButton(onClick = onSkipClick)\n            }\n        },\n        attributionContent = gameCategory.dataSourceAttribution?.let { data ->\n            {\n                DataSourceAttributionContent(\n                    text = data.text,\n                    imageUrl = data.logo\n                )\n            }\n        }\n    )\n}\n\n@OptIn(ExperimentalMaterial3Api::class)\n@Composable\n@ExperimentalAnimationApi\nprivate fun ComparisonQuizContainer(\n    modifier: Modifier = Modifier,\n    verticalContent: Boolean,\n    animationState: AnimationState,\n    backIconContent: @Composable () -> Unit,\n    descriptionContent: @Composable () -> Unit,\n    firstQuestionContent: @Composable BoxScope.() -> Unit,\n    secondQuestionContent: @Composable BoxScope.() -> Unit,\n    midContent: @Composable () -> Unit,\n    attributionContent: (@Composable () -> Unit)? = null,\n    skipButtonContent: (@Composable () -> Unit)? = null\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val mainContentTransition = updateTransition(\n        targetState = animationState,\n        label = \"Main Content\"\n    )\n\n    val reusableMidContent = remember(midContent) { movableContentOf(midContent) }\n\n    Scaffold(\n        modifier = modifier,\n        topBar = {\n            TopAppBar(\n                title = descriptionContent,\n                navigationIcon = backIconContent,\n                actions = {\n                    if (skipButtonContent != null) {\n                        skipButtonContent()\n                    }\n                }\n            )\n        }\n    ) { innerPadding ->\n        if (verticalContent) {\n            Column(\n                modifier = Modifier\n                    .padding(innerPadding + PaddingValues(spaceMedium))\n                    .fillMaxSize(),\n                verticalArrangement = Arrangement.SpaceBetween\n            ) {\n                mainContentTransition.AnimatedVisibility(\n                    visible = { state -> state != AnimationState.StartGame },\n                    modifier = Modifier\n                        .fillMaxWidth()\n                        .weight(1f),\n                    enter = fadeIn() + slideInHorizontally(),\n                    exit = fadeOut() + slideOutHorizontally()\n                ) {\n                    Box(\n                        modifier = Modifier.fillMaxSize(),\n                        content = firstQuestionContent\n                    )\n                }\n                Spacer(modifier = Modifier.height(spaceMedium))\n                reusableMidContent()\n                Spacer(modifier = Modifier.height(spaceMedium))\n                mainContentTransition.AnimatedVisibility(\n                    visible = { state -> state != AnimationState.StartGame },\n                    modifier = Modifier\n                        .fillMaxWidth()\n                        .weight(1f),\n                    // Make this animation from the opposite direction\n                    enter = fadeIn() + slideInHorizontally(\n                        initialOffsetX = { it }\n                    ),\n                    exit = fadeOut() + slideOutHorizontally(\n                        targetOffsetX = { it }\n                    )\n                ) {\n                    Box(\n                        modifier = Modifier.fillMaxSize(),\n                        content = secondQuestionContent\n                    )\n                }\n                attributionContent?.let { content ->\n                    Spacer(modifier = Modifier.height(spaceMedium))\n                    content()\n                }\n            }\n        } else {\n            Column(\n                modifier = Modifier\n                    .padding(innerPadding + PaddingValues(spaceMedium))\n                    .fillMaxSize()\n            ) {\n                Row(\n                    modifier = Modifier.weight(1f)\n                ) {\n                    mainContentTransition.AnimatedVisibility(\n                        visible = { state -> state != AnimationState.StartGame },\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .weight(1f),\n                        enter = fadeIn() + slideInVertically(),\n                        exit = fadeOut() + slideOutVertically()\n                    ) {\n                        Box(\n                            modifier = Modifier.fillMaxSize(),\n                            content = firstQuestionContent\n                        )\n                    }\n                    Spacer(modifier = Modifier.width(spaceMedium))\n                    reusableMidContent()\n                    Spacer(modifier = Modifier.width(spaceMedium))\n                    mainContentTransition.AnimatedVisibility(\n                        visible = { state -> state != AnimationState.StartGame },\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .weight(1f),\n                        enter = fadeIn() + slideInVertically(\n                            initialOffsetY = { it }\n                        ),\n                        exit = fadeOut() + slideOutVertically(\n                            targetOffsetY = { it }\n                        )\n                    ) {\n                        Box(\n                            modifier = Modifier.fillMaxSize(),\n                            content = secondQuestionContent\n                        )\n                    }\n                }\n                attributionContent?.let { attribution ->\n                    Spacer(modifier = Modifier.height(spaceMedium))\n                    attribution()\n                }\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun DataSourceAttributionContent(\n    modifier: Modifier = Modifier,\n    text: String,\n    imageUrl: String? = null\n) {\n    Row(\n        modifier = modifier,\n        verticalAlignment = Alignment.CenterVertically\n    ) {\n        Text(\n            text = text,\n            style = MaterialTheme.typography.bodyMedium,\n        )\n        if (imageUrl != null) {\n            Spacer(modifier = Modifier.width(MaterialTheme.spacing.small))\n            AsyncImage(\n                model = imageUrl,\n                contentDescription = \"Logo of the data source\",\n                modifier = Modifier\n                    .size(24.dp)\n                    .clip(MaterialTheme.shapes.extraSmall)\n            )\n        }\n    }\n}\n\nprivate const val ANIMATIONS_DELAY = 1000L\nprivate const val NAV_TO_RESULTS_DELAY_MILLIS = 250L\n\n@Composable\n@PreviewScreenSizes\n@OptIn(\n    ExperimentalMaterial3Api::class,\n    ExperimentalMaterial3WindowSizeClassApi::class,\n    ExperimentalAnimationApi::class\n)\nprivate fun ComparisonQuizScreenPreview() {\n    val question1 = ComparisonQuizItem(\n        title = \"NewQuiz\",\n        value = 3245.0,\n        imgUri = emptyJavaURI()\n    )\n\n    val question2 = ComparisonQuizItem(\n        title = \"NewSocial\",\n        value = 23445.0,\n        imgUri = emptyJavaURI()\n    )\n\n    val configuration = LocalConfiguration.current\n    val screenHeight = configuration.screenHeightDp.dp\n    val screenWidth = configuration.screenWidthDp.dp\n    val windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(screenWidth, screenHeight))\n\n    val category = ComparisonQuizCategory(\n        name = \"Social\".toUiText(),\n        description = \"Social media\",\n        image = \"\",\n        id = \"social\",\n        questionDescription = ComparisonQuizCategory.QuestionDescription(\n            greater = \"Which one is more popular?\",\n            less = \"Which one is less popular?\"\n        ),\n        dataSourceAttribution = ComparisonQuizCategory.DataSourceAttribution(\n            text = \"Data from NewQuiz\"\n        ),\n        formatType = NumberFormatType.DEFAULT\n    )\n\n    NewQuizTheme {\n        Surface {\n            ComparisonQuizScreenImpl(\n                uiState = ComparisonQuizUiState(\n                    currentQuestion = ComparisonQuizQuestion(\n                        questions = question1 to question2,\n                        categoryId = category.id,\n                        comparisonMode = ComparisonMode.GREATER\n                    ),\n                    gameDescription = \"Which one is more popular?\",\n                    gameCategory = category,\n                    userAvailable = true\n                ),\n                windowSizeClass = windowSizeClass,\n                animationState = AnimationState.Normal,\n                isFromMaze = false,\n                onEvent = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/ComparisonQuizUiEvent.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\n\ninterface ComparisonQuizUiEvent {\n    @Keep\n    data class OnAnswerClick(\n        val item: ComparisonQuizItem\n    ) : ComparisonQuizUiEvent\n\n    object ShowSkipQuestionDialog : ComparisonQuizUiEvent\n\n    object DismissSkipQuestionDialog : ComparisonQuizUiEvent\n\n    object SkipQuestion : ComparisonQuizUiEvent\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/ComparisonQuizUiState.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizHelperValueState\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\nimport com.infinitepower.newquiz.model.regional_preferences.RegionalPreferences\n\n@Keep\ndata class ComparisonQuizUiState(\n    val currentQuestion: ComparisonQuizQuestion? = null,\n    val gameCategory: ComparisonQuizCategory? = null,\n    val comparisonMode: ComparisonMode? = null,\n    val gameDescription: String? = null,\n    val currentPosition: Int = 0,\n    val highestPosition: Int = 0,\n    val isGameOver: Boolean = false,\n    val isLastQuestionCorrect: Boolean = false,\n    val userAvailable: Boolean = false,\n    val userDiamonds: Int = -1,\n    val userDiamondsLoading: Boolean = false,\n    val skipCost: UInt = 0u,\n    val firstItemHelperValueState: ComparisonQuizHelperValueState = ComparisonQuizHelperValueState.HIDDEN,\n    val regionalPreferences: RegionalPreferences = RegionalPreferences()\n)\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/ComparisonQuizViewModel.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui\n\nimport android.util.Log\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport androidx.work.WorkManager\nimport com.infinitepower.newquiz.comparison_quiz.core.workers.ComparisonQuizEndGameWorker\nimport com.infinitepower.newquiz.comparison_quiz.navArgs\nimport com.infinitepower.newquiz.core.game.ComparisonQuizCore\nimport com.infinitepower.newquiz.core.ui.SnackbarController\nimport com.infinitepower.newquiz.core.user_services.InsufficientDiamondsException\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.data.worker.UpdateGlobalEventDataWorker\nimport com.infinitepower.newquiz.domain.repository.UserConfigRepository\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.distinctUntilChangedBy\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\nimport kotlinx.coroutines.flow.stateIn\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\nprivate const val TAG = \"ComparisonQuizViewModel\"\n\n@HiltViewModel\nclass ComparisonQuizViewModel @Inject constructor(\n    private val comparisonQuizCore: ComparisonQuizCore,\n    savedStateHandle: SavedStateHandle,\n    comparisonQuizRepository: ComparisonQuizRepository,\n    private val workManager: WorkManager,\n    private val recentCategoriesRepository: RecentCategoriesRepository,\n    private val userService: UserService,\n    private val mazeQuizRepository: MazeQuizRepository,\n    private val userConfigRepository: UserConfigRepository\n) : ViewModel() {\n    private val navArgs: ComparisonQuizScreenNavArg = savedStateHandle.navArgs()\n\n    private val _uiState = MutableStateFlow(ComparisonQuizUiState())\n    val uiState = combine(\n        _uiState,\n        comparisonQuizRepository.getHighestPositionFlow(categoryId = navArgs.categoryId),\n        comparisonQuizCore.quizDataFlow\n    ) { uiState, highestPosition, quizData ->\n        val currentPosition = quizData.currentPosition\n\n        // Get the highest position between the current position and the highest position.\n        // The highest position is updated when the game is over.\n        val currentHighestPosition = maxOf(currentPosition, highestPosition)\n\n        uiState.copy(\n            currentQuestion = quizData.currentQuestion,\n            gameDescription = quizData.questionDescription,\n            currentPosition = currentPosition,\n            isGameOver = quizData.isGameOver,\n            isLastQuestionCorrect = quizData.isLastQuestionCorrect,\n            firstItemHelperValueState = quizData.firstItemHelperValueState,\n            highestPosition = currentHighestPosition\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(),\n        initialValue = ComparisonQuizUiState()\n    )\n\n    init {\n        // Start game\n        viewModelScope.launch {\n            val category = comparisonQuizRepository.getCategoryById(navArgs.categoryId)\n            if (category == null) {\n                Log.e(TAG, \"Category with id ${navArgs.categoryId} not found\")\n                comparisonQuizCore.endGame()\n                SnackbarController.sendShortMessage(\"Category not found\")\n                return@launch\n            }\n\n            val comparisonMode = navArgs.comparisonMode\n\n            // Update initial state with data that don't change during the game.\n            _uiState.update { currentState ->\n                currentState.copy(\n                    gameCategory = category,\n                    comparisonMode = comparisonMode,\n                    skipCost = comparisonQuizCore.skipCost,\n                    userAvailable = userService.userAvailable(),\n                    regionalPreferences = userConfigRepository.getRegionalPreferences()\n                )\n            }\n\n            comparisonQuizCore.initializeGame(\n                initializationData = ComparisonQuizCore.InitializationData(\n                    category = category,\n                    comparisonMode = comparisonMode,\n                    initialItems = navArgs.initialItems\n                )\n            )\n\n            launch {\n                recentCategoriesRepository.addComparisonCategory(category.id)\n\n                UpdateGlobalEventDataWorker.enqueueWork(\n                    workManager = workManager,\n                    GameEvent.ComparisonQuiz.PlayWithComparisonMode(comparisonMode),\n                    GameEvent.ComparisonQuiz.PlayQuizWithCategory(category.id)\n                )\n            }\n        }\n\n        comparisonQuizCore\n            .quizDataFlow\n            .distinctUntilChangedBy { it.isGameOver }\n            .onEach { quizData ->\n                Log.d(TAG, \"Current question: ${quizData.currentQuestion}\")\n\n                if (quizData.isGameOver) {\n                    Log.d(TAG, \"Game over, with position ${quizData.currentPosition}\")\n\n                    if (navArgs.mazeItemId != null && quizData.isLastQuestionCorrect) {\n                        mazeQuizRepository.completeMazeItem(navArgs.mazeItemId)\n                    }\n\n                    UpdateGlobalEventDataWorker.enqueueWork(\n                        workManager = workManager,\n                        GameEvent.ComparisonQuiz.PlayAndGetScore(quizData.currentPosition)\n                    )\n\n                    ComparisonQuizEndGameWorker.enqueueWork(\n                        workManager = workManager,\n                        categoryId = navArgs.categoryId,\n                        comparisonMode = navArgs.comparisonMode,\n                        endPosition = quizData.currentPosition,\n                        skippedAnswers = quizData.skippedAnswers\n                    )\n                }\n            }.launchIn(viewModelScope)\n    }\n\n    fun onEvent(event: ComparisonQuizUiEvent) {\n        when (event) {\n            is ComparisonQuizUiEvent.OnAnswerClick -> {\n                viewModelScope.launch {\n                    comparisonQuizCore.onAnswerClicked(event.item)\n                }\n            }\n\n            is ComparisonQuizUiEvent.ShowSkipQuestionDialog -> getUserDiamonds()\n            is ComparisonQuizUiEvent.DismissSkipQuestionDialog -> {\n                _uiState.update { currentState ->\n                    currentState.copy(\n                        userDiamonds = -1,\n                        userDiamondsLoading = false\n                    )\n                }\n            }\n\n            is ComparisonQuizUiEvent.SkipQuestion -> {\n                viewModelScope.launch {\n                    try {\n                        comparisonQuizCore.skip()\n                    } catch (e: InsufficientDiamondsException) {\n                        e.printStackTrace()\n                        SnackbarController.sendShortMessage(\"Insufficient diamonds\")\n                    }\n                }\n            }\n        }\n    }\n\n    private fun getUserDiamonds() = viewModelScope.launch {\n        _uiState.update { currentState ->\n            currentState.copy(userDiamondsLoading = true)\n        }\n\n        val userDiamonds = userService.getUserDiamonds()\n\n        _uiState.update { currentState ->\n            currentState.copy(\n                userDiamonds = userDiamonds.toInt(),\n                userDiamondsLoading = false\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/components/ComparisonItem.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui.components\n\nimport android.net.Uri\nimport androidx.compose.animation.AnimatedContent\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.VerticalDivider\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.dp\nimport coil.ImageLoader\nimport coil.compose.AsyncImage\nimport coil.decode.SvgDecoder\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.util.toAndroidUri\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizHelperValueState\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\n\n@Composable\ninternal fun ComparisonItem(\n    modifier: Modifier = Modifier,\n    onClick: () -> Unit,\n    item: ComparisonQuizItem,\n    helperContentAlignment: Alignment,\n    helperValue: String,\n    helperValueState: ComparisonQuizHelperValueState = ComparisonQuizHelperValueState.HIDDEN\n) {\n    ComparisonItem(\n        modifier = modifier,\n        title = item.title,\n        image = item.imgUri.toAndroidUri(),\n        helperValue = helperValue,\n        helperContentAlignment = helperContentAlignment,\n        helperValueState = helperValueState,\n        onClick = onClick\n    )\n}\n\n@Composable\ninternal fun ComparisonItem(\n    modifier: Modifier = Modifier,\n    onClick: () -> Unit,\n    title: String,\n    image: Uri,\n    helperValue: String,\n    helperContentAlignment: Alignment,\n    helperValueState: ComparisonQuizHelperValueState\n) {\n    val spaceExtraSmall = MaterialTheme.spacing.extraSmall\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val helperColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.7f)\n\n    val context = LocalContext.current\n\n    val imageLoader = ImageLoader\n        .Builder(context)\n        .components {\n            add(SvgDecoder.Factory())\n        }.build()\n\n    Surface(\n        modifier = modifier,\n        shape = MaterialTheme.shapes.large,\n        onClick = onClick,\n        tonalElevation = 8.dp\n    ) {\n        Box(\n            modifier = Modifier.fillMaxSize()\n        ) {\n            AsyncImage(\n                model = image,\n                contentDescription = null,\n                contentScale = ContentScale.Crop,\n                modifier = Modifier.fillMaxSize(),\n                imageLoader = imageLoader\n            )\n\n            Surface(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .align(helperContentAlignment)\n                    .padding(spaceMedium),\n                tonalElevation = 8.dp,\n                shape = MaterialTheme.shapes.large,\n                color = helperColor\n            ) {\n                Row(\n                    verticalAlignment = Alignment.CenterVertically,\n                    horizontalArrangement = Arrangement.SpaceAround,\n                    modifier = Modifier.padding(spaceMedium)\n                ) {\n                    Text(\n                        modifier = Modifier.weight(1f),\n                        text = title,\n                        style = MaterialTheme.typography.titleMedium,\n                        maxLines = 1,\n                        overflow = TextOverflow.Ellipsis,\n                        textAlign = TextAlign.Center\n                    )\n                    if (helperValueState == ComparisonQuizHelperValueState.NORMAL) {\n                        Spacer(modifier = Modifier.width(spaceExtraSmall))\n                        VerticalDivider(modifier = Modifier.height(24.dp))\n                        Spacer(modifier = Modifier.width(spaceExtraSmall))\n                        AnimatedContent(\n                            targetState = helperValue,\n                            label = \"helper value\",\n                            modifier = Modifier.weight(1f),\n                        ) { value ->\n                            Text(\n                                text = value,\n                                style = MaterialTheme.typography.titleMedium,\n                                maxLines = 1,\n                                textAlign = TextAlign.Center\n                            )\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun ComparisonQuizScreenPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) firstItem: Boolean\n) {\n    val alignment = if (firstItem) Alignment.TopCenter else Alignment.BottomCenter\n\n    NewQuizTheme {\n        Surface {\n            ComparisonItem(\n                modifier = Modifier\n                    .padding(16.dp)\n                    .size(400.dp),\n                title = \"NewQuizsssssssssssssssssssssssssssssssssss\",\n                image = Uri.EMPTY,\n                helperValue = \"12,345\",\n                onClick = {},\n                helperContentAlignment = alignment,\n                helperValueState = ComparisonQuizHelperValueState.NORMAL\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/components/ComparisonMidContent.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxHeight\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.movableContentOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.comparison_quiz.ui.AnimationState\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun ComparisonMidContent(\n    modifier: Modifier = Modifier,\n    questionPosition: Int,\n    highestPosition: Int,\n    verticalContent: Boolean,\n    animationState: AnimationState\n) {\n    ComparisonMidContainer(\n        modifier = modifier,\n        verticalContent = verticalContent,\n        currentPositionContent = {\n            Text(\n                text = stringResource(id = CoreR.string.position_n, questionPosition),\n                style = MaterialTheme.typography.titleMedium,\n                textAlign = TextAlign.Center\n            )\n        },\n        highestPositionContent = {\n            Text(\n                text = stringResource(id = CoreR.string.highest_n, highestPosition),\n                style = MaterialTheme.typography.titleMedium,\n                textAlign = TextAlign.Center\n            )\n        },\n        midContent = { MiddleCircle(animationState = animationState) }\n    )\n}\n\n@Composable\nprivate fun ComparisonMidContainer(\n    modifier: Modifier = Modifier,\n    verticalContent: Boolean,\n    currentPositionContent: @Composable () -> Unit,\n    highestPositionContent: @Composable () -> Unit,\n    midContent: @Composable () -> Unit\n) {\n    val movableCurrentPositionContent = remember(currentPositionContent) {\n        movableContentOf(currentPositionContent)\n    }\n    val movableHighestPositionContent = remember(highestPositionContent) {\n        movableContentOf(highestPositionContent)\n    }\n    val movableMidContent = remember(midContent) {\n        movableContentOf(midContent)\n    }\n\n    if (verticalContent) {\n        Row(\n            modifier = modifier.fillMaxWidth(),\n            horizontalArrangement = Arrangement.SpaceEvenly,\n            verticalAlignment = Alignment.CenterVertically\n        ) {\n            movableCurrentPositionContent()\n            movableMidContent()\n            movableHighestPositionContent()\n        }\n    } else {\n        Column(\n            modifier = modifier.fillMaxHeight(),\n            verticalArrangement = Arrangement.SpaceEvenly,\n            horizontalAlignment = Alignment.CenterHorizontally\n        ) {\n            movableCurrentPositionContent()\n            movableMidContent()\n            movableHighestPositionContent()\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun ComparisonMidContentPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) verticalContent: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            ComparisonMidContent(\n                questionPosition = 1,\n                highestPosition = 10,\n                verticalContent = verticalContent,\n                modifier = Modifier.padding(16.dp),\n                animationState = AnimationState.Normal\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/components/GameOverContent.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui.components\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Card\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedButton\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.toArgb\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.unit.dp\nimport com.airbnb.lottie.LottieProperty\nimport com.airbnb.lottie.SimpleColorFilter\nimport com.airbnb.lottie.compose.LottieAnimation\nimport com.airbnb.lottie.compose.LottieCompositionSpec\nimport com.airbnb.lottie.compose.rememberLottieComposition\nimport com.airbnb.lottie.compose.rememberLottieDynamicProperties\nimport com.airbnb.lottie.compose.rememberLottieDynamicProperty\nimport com.infinitepower.newquiz.core.R\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun GameOverContent(\n    modifier: Modifier = Modifier,\n    scorePosition: Int,\n    highestPosition: Int,\n    onBackClick: () -> Unit = {},\n    onPlayAgainClick: () -> Unit = {}\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n    val spaceLarge = MaterialTheme.spacing.large\n\n    val trophySpec = LottieCompositionSpec.RawRes(R.raw.trophy2)\n    val trophyLottieComposition by rememberLottieComposition(spec = trophySpec)\n\n    val dynamicProperties = rememberLottieDynamicProperties(\n        rememberLottieDynamicProperty(\n            property = LottieProperty.COLOR_FILTER,\n            value = SimpleColorFilter(MaterialTheme.colorScheme.primary.toArgb()),\n            keyPath = arrayOf(\"**\")\n        ),\n    )\n\n    LazyColumn(\n        modifier = modifier.fillMaxSize(),\n        horizontalAlignment = Alignment.CenterHorizontally,\n        contentPadding = PaddingValues(\n            vertical = MaterialTheme.spacing.extraLarge,\n            horizontal = spaceLarge\n        )\n    ) {\n        item(\"headline\") {\n            Text(\n                text = stringResource(id = CoreR.string.game_over),\n                style = MaterialTheme.typography.headlineLarge\n            )\n            Spacer(modifier = Modifier.height(spaceLarge))\n        }\n\n        item(\"animation_image\") {\n            LottieAnimation(\n                composition = trophyLottieComposition,\n                modifier = Modifier.size(200.dp),\n                dynamicProperties = dynamicProperties\n            )\n            Spacer(modifier = Modifier.height(spaceLarge))\n        }\n\n        item(\"current_score\") {\n            Text(\n                text = stringResource(id = CoreR.string.your_score).uppercase(),\n                style = MaterialTheme.typography.titleMedium\n            )\n            Spacer(modifier = Modifier.height(spaceMedium))\n            Card(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .padding(horizontal = spaceLarge)\n            ){\n                Box(\n                    contentAlignment = Alignment.Center,\n                    modifier = Modifier.fillMaxWidth()\n                ) {\n                    Text(\n                        text = scorePosition.toString(),\n                        style = MaterialTheme.typography.headlineLarge,\n                        modifier = Modifier.padding(vertical = MaterialTheme.spacing.small)\n                    )\n                }\n            }\n            Spacer(modifier = Modifier.height(spaceLarge))\n        }\n\n        item(\"highest_score\") {\n            Text(\n                text = stringResource(id = CoreR.string.highest_score).uppercase(),\n                style = MaterialTheme.typography.titleMedium\n            )\n            Spacer(modifier = Modifier.height(spaceMedium))\n            Card(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .padding(horizontal = spaceLarge)\n            ){\n                Box(\n                    contentAlignment = Alignment.Center,\n                    modifier = Modifier.fillMaxWidth()\n                ) {\n                    Text(\n                        text = highestPosition.toString(),\n                        style = MaterialTheme.typography.headlineLarge,\n                        modifier = Modifier.padding(vertical = MaterialTheme.spacing.small)\n                    )\n                }\n            }\n            Spacer(modifier = Modifier.height(MaterialTheme.spacing.extraLarge))\n        }\n\n        item(\"buttons\") {\n            Row(\n                verticalAlignment = Alignment.CenterVertically,\n                modifier = Modifier.fillMaxWidth()\n            ) {\n                OutlinedButton(\n                    onClick = onBackClick,\n                    modifier = Modifier.weight(1f)\n                ) {\n                    Text(text = stringResource(id = CoreR.string.back))\n                }\n                Spacer(modifier = Modifier.width(spaceMedium))\n                Button(\n                    onClick = onPlayAgainClick,\n                    modifier = Modifier.weight(1f)\n                ) {\n                    Text(text = stringResource(id = CoreR.string.play_again))\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun GameOverContentPreview() {\n    NewQuizTheme {\n        Surface {\n            GameOverContent(\n                scorePosition = 3,\n                highestPosition = 5\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/main/java/com/infinitepower/newquiz/comparison_quiz/ui/components/MiddleCircle.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.ui.components\n\nimport androidx.compose.animation.AnimatedContent\nimport androidx.compose.animation.animateColor\nimport androidx.compose.animation.core.Transition\nimport androidx.compose.animation.core.animateDp\nimport androidx.compose.animation.core.tween\nimport androidx.compose.animation.core.updateTransition\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.contentColorFor\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.comparison_quiz.ui.AnimationState\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.CustomColor\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.extendedColors\n\n/**\n * A circle with the text \"or\" in the middle.\n * Used to separate the two options in the comparison quiz.\n *\n * When the question is answered, the circle is filled with the correct color\n * with an animation.\n */\n@Composable\ninternal fun MiddleCircle(\n    modifier: Modifier = Modifier,\n    animationState: AnimationState = AnimationState.Normal\n) {\n    val animationTransition = updateTransition(\n        targetState = animationState,\n        label = \"Middle Circle\"\n    )\n\n    val correctQuestionColors = MaterialTheme.extendedColors.getColorsByKey(\n        key = CustomColor.Key.Green\n    )\n\n    val containerColor by animationTransition.animateColor(\n        transitionSpec = { tween(durationMillis = ANIMATION_DURATION_MILLIS) },\n        label = \"Container Color\"\n    ) { state ->\n        when (state) {\n            AnimationState.StartGame -> MaterialTheme.colorScheme.surface\n            AnimationState.Normal -> MaterialTheme.colorScheme.tertiary\n            AnimationState.NextQuestion -> correctQuestionColors.color\n        }\n    }\n\n    val contentColor by animationTransition.animateColor(\n        transitionSpec = { tween(durationMillis = ANIMATION_DURATION_MILLIS) },\n        label = \"Content Color\"\n    ) { state ->\n        when (state) {\n            AnimationState.StartGame -> MaterialTheme.colorScheme.onSurface\n            AnimationState.Normal -> MaterialTheme.colorScheme.onTertiary\n            AnimationState.NextQuestion -> correctQuestionColors.onColor\n        }\n    }\n\n    val tonalElevation by animationTransition.animateDp(\n        transitionSpec = { tween(durationMillis = ANIMATION_DURATION_MILLIS) },\n        label = \"Tonal Elevation\"\n    ) { state ->\n        when (state) {\n            AnimationState.StartGame -> 0.dp\n            AnimationState.Normal, AnimationState.NextQuestion -> DEFAULT_TONAL_ELEVATION\n        }\n    }\n\n    MiddleCircle(\n        modifier = modifier,\n        animationTransition = animationTransition,\n        containerColor = containerColor,\n        contentColor = contentColor,\n        tonalElevation = tonalElevation\n    )\n}\n\nprivate const val ANIMATION_DURATION_MILLIS = 310\nprivate val DEFAULT_TONAL_ELEVATION = 2.dp\n\n@Composable\nprivate fun MiddleCircle(\n    modifier: Modifier = Modifier,\n    animationTransition: Transition<AnimationState>,\n    containerColor: Color = MaterialTheme.colorScheme.tertiary,\n    contentColor: Color = contentColorFor(containerColor),\n    tonalElevation: Dp = DEFAULT_TONAL_ELEVATION\n) {\n    Surface(\n        shape = CircleShape,\n        modifier = modifier.size(48.dp),\n        color = containerColor,\n        contentColor = contentColor,\n        tonalElevation = tonalElevation\n    ) {\n        Box(\n            modifier = Modifier.fillMaxSize(),\n            contentAlignment = Alignment.Center\n        ) {\n            animationTransition.AnimatedContent { state ->\n                when (state) {\n                    AnimationState.StartGame -> Unit\n\n                    AnimationState.Normal -> {\n                        Text(\n                            text = stringResource(id = R.string.or).uppercase(),\n                            style = MaterialTheme.typography.headlineSmall\n                        )\n                    }\n\n                    AnimationState.NextQuestion -> {\n                        Icon(\n                            imageVector = Icons.Rounded.Check,\n                            contentDescription = null,\n                            modifier = Modifier.size(24.dp)\n                        )\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun MiddleCirclePreview() {\n    NewQuizTheme {\n        Surface {\n            MiddleCircle(\n                modifier = Modifier.padding(16.dp),\n                animationState = AnimationState.Normal\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "comparison-quiz/src/test/java/com/infinitepower/newquiz/comparison_quiz/core/ComparisonQuizCoreImplTest.kt",
    "content": "package com.infinitepower.newquiz.comparison_quiz.core\n\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.PreferenceDataStoreFactory\nimport androidx.datastore.preferences.core.Preferences\nimport app.cash.turbine.test\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.analytics.NoOpAnalyticsHelper\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.datastore.common.LocalUserCommon\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.PreferencesDatastoreManager\nimport com.infinitepower.newquiz.core.game.ComparisonQuizCore\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.testing.domain.FakeGameResultDao\nimport com.infinitepower.newquiz.core.testing.utils.mockAndroidLog\nimport com.infinitepower.newquiz.core.user_services.InsufficientDiamondsException\nimport com.infinitepower.newquiz.core.user_services.LocalUserServiceImpl\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.core.user_services.data.xp.ComparisonQuizXpGeneratorImpl\nimport com.infinitepower.newquiz.core.user_services.data.xp.MultiChoiceQuizXpGeneratorImpl\nimport com.infinitepower.newquiz.core.user_services.data.xp.WordleXpGeneratorImpl\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizHelperValueState\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\nimport com.infinitepower.newquiz.model.toUiText\nimport io.mockk.coEvery\nimport io.mockk.confirmVerified\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.verify\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.api.io.TempDir\nimport java.io.File\nimport java.net.URI\n\n/**\n * Unit tests for [ComparisonQuizCoreImpl].\n */\ninternal class ComparisonQuizCoreImplTest {\n    private lateinit var comparisonQuizCoreImpl: ComparisonQuizCoreImpl\n\n    private val comparisonQuizRepository = mockk<ComparisonQuizRepository>()\n    private val remoteConfig = mockk<RemoteConfig>()\n\n    private lateinit var userService: UserService\n\n    @TempDir\n    lateinit var tmpDir: File\n\n    private lateinit var dataStoreManager: DataStoreManager\n    private lateinit var gameResultDao: GameResultDao\n\n    private val firstItemHelperValueState = ComparisonQuizHelperValueState.HIDDEN\n\n    @BeforeEach\n    fun setup() {\n        mockAndroidLog()\n        val testDataStore: DataStore<Preferences> = PreferenceDataStoreFactory.create(\n            produceFile = { File(tmpDir, \"user.preferences_pb\") }\n        )\n\n        dataStoreManager = PreferencesDatastoreManager(testDataStore)\n\n        gameResultDao = FakeGameResultDao()\n\n        userService = LocalUserServiceImpl(\n            dataStoreManager = dataStoreManager,\n            remoteConfig = remoteConfig,\n            gameResultDao = gameResultDao,\n            multiChoiceXpGenerator = MultiChoiceQuizXpGeneratorImpl(remoteConfig),\n            wordleXpGenerator = WordleXpGeneratorImpl(remoteConfig),\n            comparisonQuizXpGenerator = ComparisonQuizXpGeneratorImpl(remoteConfig)\n        )\n\n        comparisonQuizCoreImpl = ComparisonQuizCoreImpl(\n            comparisonQuizRepository = comparisonQuizRepository,\n            remoteConfig = remoteConfig,\n            analyticsHelper = NoOpAnalyticsHelper,\n            userService = userService\n        )\n\n        every {\n            remoteConfig.getString(\"comparison_quiz_first_item_helper_value\")\n        } returns firstItemHelperValueState.name\n    }\n\n    @Test\n    fun `initializeGame should emit correct data`() = runTest {\n        val initialData = getInitializationData()\n        val uriMock = getUriMock()\n\n        val expectedQuestions = listOf(\n            ComparisonQuizItem(\n                title = \"Question 1\",\n                value = 1.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 2\",\n                value = 2.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 3\",\n                value = 3.0,\n                imgUri = uriMock\n            )\n        )\n\n        coEvery {\n            comparisonQuizRepository.getQuestions(category = initialData.category)\n        } returns expectedQuestions\n\n        comparisonQuizCoreImpl.initializeGame(initialData)\n\n        val quizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        assertThat(quizData.comparisonMode).isEqualTo(initialData.comparisonMode)\n        assertThat(quizData.questions).contains(expectedQuestions[2])\n        assertThat(quizData.firstItemHelperValueState).isEqualTo(firstItemHelperValueState)\n\n        val expectedCurrentQuestion = ComparisonQuizQuestion(\n            questions = expectedQuestions[0] to expectedQuestions[1],\n            categoryId = \"id\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        // verify current question\n        assertThat(quizData.currentQuestion).isNotNull()\n        assertThat(quizData.currentQuestion).isEqualTo(expectedCurrentQuestion)\n    }\n\n    @Test\n    fun `initializeGame should end game when error in data request`() = runTest {\n        val initialData = getInitializationData()\n\n        coEvery {\n            comparisonQuizRepository.getQuestions(category = initialData.category)\n        } returns emptyList()\n\n        comparisonQuizCoreImpl.initializeGame(initialData)\n\n        comparisonQuizCoreImpl.quizDataFlow.test {\n            val quizData = awaitItem()\n            assertThat(quizData.isGameOver).isTrue()\n            assertThat(quizData.currentQuestion).isNull()\n        }\n    }\n\n    @Test\n    fun `onAnswerClicked should check the correct answer and move to the next question`() = runTest {\n        val initialData = getInitializationData(\n            comparisonMode = ComparisonMode.LESSER\n        )\n        val uriMock = getUriMock()\n\n        val expectedQuestions = listOf(\n            ComparisonQuizItem(\n                title = \"Question 1\",\n                value = 1.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 2\",\n                value = 2.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 3\",\n                value = 3.0,\n                imgUri = uriMock\n            )\n        )\n\n        coEvery {\n            comparisonQuizRepository.getQuestions(category = initialData.category)\n        } returns expectedQuestions\n\n        comparisonQuizCoreImpl.initializeGame(initialData)\n\n        // verify current question\n        val quizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        val currentQuestion = quizData.currentQuestion\n\n        assertThat(quizData.comparisonMode).isEqualTo(initialData.comparisonMode)\n\n        // Check if the helper value is correct\n        assertThat(quizData.firstItemHelperValueState).isEqualTo(firstItemHelperValueState)\n\n        assertThat(quizData.isGameOver).isFalse()\n        assertThat(currentQuestion).isNotNull()\n        require(currentQuestion != null)\n\n        assertThat(quizData.questions).hasSize(1)\n        assertThat(quizData.questions).contains(expectedQuestions[2])\n\n        assertThat(currentQuestion.questions.first).isEqualTo(expectedQuestions[0])\n        assertThat(currentQuestion.questions.second).isEqualTo(expectedQuestions[1])\n\n        // verify answer\n        comparisonQuizCoreImpl.onAnswerClicked(currentQuestion.questions.first)\n\n        val newQuizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        val newCurrentQuestion = newQuizData.currentQuestion\n\n        assertThat(newQuizData.isGameOver).isFalse()\n        assertThat(newCurrentQuestion).isNotNull()\n        require(newCurrentQuestion != null)\n\n        assertThat(newQuizData.questions).isEmpty()\n\n        assertThat(newCurrentQuestion.questions.first).isEqualTo(expectedQuestions[1])\n        assertThat(newCurrentQuestion.questions.second).isEqualTo(expectedQuestions[2])\n\n        // Check if the helper value is changed\n        assertThat(newQuizData.firstItemHelperValueState).isNotEqualTo(firstItemHelperValueState)\n        assertThat(newQuizData.firstItemHelperValueState).isEqualTo(ComparisonQuizHelperValueState.NORMAL)\n    }\n\n    // Test when the user gets the answer wrong\n    @Test\n    fun `onAnswerClicked should check the wrong answer and move to the next question`() = runTest {\n        val initialData = getInitializationData(\n            comparisonMode = ComparisonMode.LESSER\n        )\n        val uriMock = getUriMock()\n\n        val expectedQuestions = listOf(\n            ComparisonQuizItem(\n                title = \"Question 1\",\n                value = 1.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 2\",\n                value = 2.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 3\",\n                value = 3.0,\n                imgUri = uriMock\n            )\n        )\n\n        coEvery {\n            comparisonQuizRepository.getQuestions(category = initialData.category)\n        } returns expectedQuestions\n\n        // Loads the initial data and starts the game\n        // This will also set the current question\n        // The answer will be wrong\n        comparisonQuizCoreImpl.initializeGame(initialData)\n\n        // Get the current data and question\n        val quizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        val currentQuestion = quizData.currentQuestion\n\n        // Check if comparison mode is correct\n        assertThat(quizData.comparisonMode).isEqualTo(initialData.comparisonMode)\n\n        // Check if the helper value is correct\n        assertThat(quizData.firstItemHelperValueState).isEqualTo(firstItemHelperValueState)\n\n        // Check if current question is not null\n        assertThat(currentQuestion).isNotNull()\n        require(currentQuestion != null)\n\n        assertThat(quizData.questions).hasSize(1)\n        assertThat(quizData.questions).contains(expectedQuestions[2])\n\n        assertThat(currentQuestion.questions.first).isEqualTo(expectedQuestions[0])\n        assertThat(currentQuestion.questions.second).isEqualTo(expectedQuestions[1])\n\n        // verify answer\n        comparisonQuizCoreImpl.onAnswerClicked(currentQuestion.questions.second)\n\n        val newQuizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        val newCurrentQuestion = newQuizData.currentQuestion\n\n        assertThat(newCurrentQuestion).isNull()\n        assertThat(newQuizData.isGameOver).isTrue()\n    }\n\n    @Test\n    fun `onAnswerClicked should end the game when no more questions is remaining`() = runTest {\n        val initialData = getInitializationData()\n        val uriMock = getUriMock()\n\n        val expectedQuestions = listOf(\n            ComparisonQuizItem(\n                title = \"Question 1\",\n                value = 1.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 2\",\n                value = 2.0,\n                imgUri = uriMock\n            )\n        )\n\n        coEvery {\n            comparisonQuizRepository.getQuestions(category = initialData.category)\n        } returns expectedQuestions\n\n        comparisonQuizCoreImpl.initializeGame(initialData)\n\n        val quizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        assertThat(quizData.questions).isEmpty()\n\n        val expectedCurrentQuestion = ComparisonQuizQuestion(\n            questions = expectedQuestions[0] to expectedQuestions[1],\n            categoryId = \"id\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        val currentQuestion = quizData.currentQuestion\n\n        // verify current question\n        assertThat(currentQuestion).isNotNull()\n        assertThat(currentQuestion).isEqualTo(expectedCurrentQuestion)\n        require(currentQuestion != null)\n\n        comparisonQuizCoreImpl.onAnswerClicked(currentQuestion.questions.second)\n\n        val newQuizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        val newCurrentQuestion = newQuizData.currentQuestion\n\n        assertThat(newCurrentQuestion).isNull()\n        assertThat(newQuizData.isGameOver).isTrue()\n    }\n\n    @Test\n    fun `endGame() should end the game`() = runTest {\n        val initialData = getInitializationData()\n        val uriMock = getUriMock()\n\n        val expectedQuestions = listOf(\n            ComparisonQuizItem(\n                title = \"Question 1\",\n                value = 1.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 2\",\n                value = 2.0,\n                imgUri = uriMock\n            )\n        )\n\n        coEvery {\n            comparisonQuizRepository.getQuestions(category = initialData.category)\n        } returns expectedQuestions\n\n        comparisonQuizCoreImpl.initializeGame(initialData)\n\n        val quizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        assertThat(quizData.questions).isEmpty()\n\n        val expectedCurrentQuestion = ComparisonQuizQuestion(\n            questions = expectedQuestions[0] to expectedQuestions[1],\n            categoryId = \"id\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        val currentQuestion = quizData.currentQuestion\n\n        // verify current question\n        assertThat(currentQuestion).isNotNull()\n        assertThat(currentQuestion).isEqualTo(expectedCurrentQuestion)\n        require(currentQuestion != null)\n\n        comparisonQuizCoreImpl.endGame()\n\n        val newQuizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        val newCurrentQuestion = newQuizData.currentQuestion\n\n        assertThat(newCurrentQuestion).isNull()\n        assertThat(newQuizData.isGameOver).isTrue()\n    }\n\n    @Test\n    fun `skip() should deduct diamonds and update quiz data`() = runTest {\n        val skipCost = 1\n        val userDiamonds = 10\n\n        val initialData = getInitializationData()\n\n        every { remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_SKIP_COST) } returns skipCost\n        every { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns userDiamonds\n        dataStoreManager.editPreference(LocalUserCommon.UserDiamonds(userDiamonds).key, userDiamonds)\n\n        val uriMock = getUriMock()\n\n        val expectedQuestions = listOf(\n            ComparisonQuizItem(\n                title = \"Question 1\",\n                value = 1.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 2\",\n                value = 2.0,\n                imgUri = uriMock\n            ),\n            ComparisonQuizItem(\n                title = \"Question 3\",\n                value = 3.0,\n                imgUri = uriMock\n            )\n        )\n\n        coEvery {\n            comparisonQuizRepository.getQuestions(category = initialData.category)\n        } returns expectedQuestions\n\n        comparisonQuizCoreImpl.initializeGame(initialData)\n\n        // verify current question\n        val quizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        val currentQuestion = quizData.currentQuestion\n\n        assertThat(quizData.comparisonMode).isEqualTo(initialData.comparisonMode)\n\n        // Check if the helper value is correct\n        assertThat(quizData.firstItemHelperValueState).isEqualTo(firstItemHelperValueState)\n\n        assertThat(quizData.isGameOver).isFalse()\n        assertThat(quizData.skippedAnswers).isEqualTo(0)\n        assertThat(currentQuestion).isNotNull()\n        require(currentQuestion != null)\n\n        comparisonQuizCoreImpl.skip()\n\n        verify(exactly = 1) { remoteConfig.getString(\"comparison_quiz_first_item_helper_value\") }\n        verify { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) }\n        verify(exactly = 1) { remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_SKIP_COST) }\n        confirmVerified(remoteConfig)\n\n        val newQuizData = comparisonQuizCoreImpl.quizDataFlow.first()\n        val newCurrentQuestion = newQuizData.currentQuestion\n\n        assertThat(newQuizData.isGameOver).isFalse()\n        assertThat(newQuizData.skippedAnswers).isEqualTo(1)\n        assertThat(newCurrentQuestion).isNotNull()\n        require(newCurrentQuestion != null)\n\n        assertThat(newQuizData.questions).isEmpty()\n\n        assertThat(newCurrentQuestion.questions.first).isEqualTo(expectedQuestions[1])\n        assertThat(newCurrentQuestion.questions.second).isEqualTo(expectedQuestions[2])\n\n        // Check if the helper value is changed\n        assertThat(newQuizData.firstItemHelperValueState).isNotEqualTo(firstItemHelperValueState)\n        assertThat(newQuizData.firstItemHelperValueState).isEqualTo(ComparisonQuizHelperValueState.NORMAL)\n    }\n\n    @Test\n    fun `skip should return when user doesn't have enough diamonds`() = runTest {\n        val skipCost = 10\n        val userDiamonds = 5\n\n        every { remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_SKIP_COST) } returns skipCost\n        every { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns userDiamonds\n        dataStoreManager.editPreference(LocalUserCommon.UserDiamonds(userDiamonds).key, userDiamonds)\n\n        val initialQuizData = comparisonQuizCoreImpl.quizDataFlow.first()\n\n        // Assert exception is thrown\n        assertThrows<InsufficientDiamondsException> {\n            comparisonQuizCoreImpl.skip()\n        }\n\n        // Verify that we called this once, one for checking if can skip\n        // We don't call it again because we don't have enough diamonds\n        verify(exactly = 1) { remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_SKIP_COST) }\n        verify(exactly = 1) { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) }\n        confirmVerified(remoteConfig)\n\n        // Check if nothing is changed\n        assertThat(userService.getUserDiamonds()).isEqualTo(userDiamonds.toUInt())\n        assertThat(comparisonQuizCoreImpl.quizDataFlow.first()).isEqualTo(initialQuizData)\n    }\n\n    private fun getInitializationData(\n        comparisonMode: ComparisonMode = ComparisonMode.GREATER\n    ) = ComparisonQuizCore.InitializationData(\n        category = ComparisonQuizCategory(\n            id = \"id\",\n            name = \"title\".toUiText(),\n            description = \"description\",\n            image = \"imageUrl\",\n            questionDescription = ComparisonQuizCategory.QuestionDescription(\n                greater = \"greater\",\n                less = \"less\"\n            ),\n            formatType = NumberFormatType.DEFAULT,\n            helperValueSuffix = \"helperValueSuffix\",\n            dataSourceAttribution = ComparisonQuizCategory.DataSourceAttribution(\n                text = \"text\",\n                logo = \"logo\"\n            )\n        ),\n        comparisonMode = comparisonMode\n    )\n\n    private fun getUriMock(): URI = URI(\"test/path\")\n}\n"
  },
  {
    "path": "compose_compiler_config.conf",
    "content": "// Consider LocalDateTime stable\njava.time.LocalDateTime\n// Consider my datalayer and all submodules stable\ncom.infinitepower.newquiz.model.**\ncom.infinitepower.newquiz.domain.repository.home.HomeCategories<*>"
  },
  {
    "path": "core/.gitignore",
    "content": "/build"
  },
  {
    "path": "core/analytics/.gitignore",
    "content": "/build"
  },
  {
    "path": "core/analytics/LOGGING_ANALYTICS.md",
    "content": "# Overview\n\nThis document describes the logging analytics events and parameters, collected by the application. \nThe events are sent anonymously to the Firebase Analytics. The user can opt out from the analytics in the settings screen.\nThese events are used to improve the application and to provide better user experience.\n\nFirebase automatically collects some [events](https://support.google.com/firebase/answer/9234069?visit_id=638163118323661106-1839920987&rd=1).\n\n# Core logging\n\n## screen_view\n\nThis event logs the current user screen.\n\n### Parameters\n\n| Parameter name |  Type  | Required | Example value | Description |\n|----------------|--------|----------|---------------|-------------|\n| screen_name    | string | No       | Home Screen   | Screen name |\n\n## level_up\n\nThis event logs the new level reached by the user.\n\n### Parameters\n\n| Parameter name |  Type  | Required | Example value | Description             |\n|----------------|--------|----------|---------------|-------------------------|\n| level          | number | Yes      | 10            | New level of the user   |\n| character      | string | No       | global        | Where the user level up |\n\n## earn_virtual_currency\n\nThis event logs the virtual currency earned by the user. This can be trigger when the user\nreached a new level or when the user completed a quiz.\n\n### Parameters\n\n| Parameter name        |  Type  | Required | Example value | Description             |\n|-----------------------|--------|----------|---------------|-------------------------|\n| virtual_currency_name | string | Yes      | diamonds      | Virtual currency name   |\n| value                 | number | Yes      | 10            | Virtual currency value  |\n\n## spend_virtual_currency\n\nThis event logs the virtual currency spent by the user. This can be trigger when the user\nbuy a skips the question.\n\n### Parameters\n\n| Parameter name        |  Type  | Required | Example value | Description             |\n|-----------------------|--------|----------|---------------|-------------------------|\n| virtual_currency_name | string | Yes      | diamonds      | Virtual currency name   |\n| value                 | number | Yes      | 10            | Virtual currency value  |\n\n# Multi choice quiz\n\n## multi_choice_game_start\n\nThis event logs the multi choice game quiz start.\n\n### Parameters\n\n| Parameter name              |  Type  | Required | Example value | Description                 |\n|-----------------------------|--------|----------|---------------|-----------------------------|\n| multi_choice_questions_size | number | Yes      | 10            | Questions size              |\n| multi_choice_category       | string | No       | flag          | Category for questions      |\n| multi_choice_difficulty     | string | No       | easy          | Difficulty for questions    |\n| maze_item_id                | string | No       | 123456        | Item id from maze game mode |\n\n## multi_choice_game_end\n\nThis event logs the multi choice game quiz end.\n\n### Parameters\n\n| Parameter name               |  Type  | Required | Example value | Description                 |\n|------------------------------|--------|----------|---------------|-----------------------------|\n| multi_choice_questions_size  | number | Yes      | 10            | Questions size              |\n| multi_choice_correct_answers | number | Yes      | 5             | Correct answers size        |\n| maze_item_id                 | string | No       | 123456        | Item id from maze game mode |\n\n## multi_choice_category_clicked\n\nThis event logs when the user clicked on a multi choice quiz category.\n\n### Parameters\n\n| Parameter name | Type   | Required | Example value | Description     |\n|----------------|--------|----------|---------------|-----------------|\n| id             | string | Yes      | flag          | The category id |\n\n## multi_choice_save_question\n\nThis event logs the user save a question from the multi choice quiz game.\n\n## multi_choice_save_question\n\nThis event logs the user downloads multi choice quiz questions to save then.\n\n## multi_choice_play_saved_questions\n\nThis event logs the user plays the saved multi choice quiz questions.\n\n### Parameters\n\n| Parameter name              | Type   | Required | Example value | Description    |\n|-----------------------------|--------|----------|---------------|----------------|\n| multi_choice_questions_size | number | Yes      | 10            | Questions size |\n\n# Wordle logging analytics\n\n## wordle_game_start\n\nThis event logs the wordle game quiz start.\n\n### Parameters\n\n| Parameter name     | Type   | Required | Example value | Description                 |\n|--------------------|--------|----------|---------------|-----------------------------|\n| wordle_word_length | number | Yes      | 5             | Word length                 |\n| wordle_max_rows    | number | Yes      | 10            | Quiz max rows               |\n| wordle_quiz_type   | string | No       | text          | The type of the wordle quiz |\n| wordle_day         | string | No       | 2019-01-01    | Wordle day mode: word date  |\n| maze_item_id       | string | No       | 123456        | Item id from maze game mode |\n\n## wordle_game_end\n\nThis event logs the wordle game quiz end.\n\n### Parameters\n\n| Parameter name          | Type   | Required | Example value | Description                 |\n|-------------------------|--------|----------|---------------|-----------------------------|\n| wordle_word_length      | number | Yes      | 5             | Word length                 |\n| wordle_max_rows         | number | Yes      | 10            | Quiz max rows               |\n| wordle_last_row         | number | Yes      | 5             | Quiz last row position      |\n| wordle_last_row_correct | string | Yes      | true          | Is quiz last row correct    |\n| wordle_quiz_type        | string | No       | text          | The type of the wordle quiz |\n| wordle_day              | string | No       | 2019-01-01    | Wordle day mode: word date  |\n| maze_item_id            | string | No       | 123456        | Item id from maze game mode |\n\n## daily_wordle_item_click\n\nThis event logs when the user click on a daily wordle item.\n\n### Parameters\n\n| Parameter name     | Type   | Required | Example value | Description                |\n|--------------------|--------|----------|---------------|----------------------------|\n| wordle_word_length | number | Yes      | 5             | Word length                |\n| day                | string | Yes      | 2019-01-01    | Wordle day mode: word date |\n\n## daily_wordle_item_complete\n\nThis event logs when the user complete a daily wordle item.\n\n### Parameters\n\n| Parameter name          | Type   | Required | Example value | Description                |\n|-------------------------|--------|----------|---------------|----------------------------|\n| wordle_word_length      | number | Yes      | 5             | Word length                |\n| wordle_last_row_correct | string | Yes      | true          | Is quiz last row correct   |\n| day                     | string | No       | 2019-01-01    | Wordle day mode: word date |\n\n# Maze\n\n## create_maze\n\nThis event logs the maze game mode creation.\n\n### Parameters\n\n| Parameter name |  Type  | Required | Example value | Description         |\n|----------------|--------|----------|---------------|---------------------|\n| seed           | number | No       | 123456        | Maze random seed    |\n| item_size      | number | No       | 10            | Maze questions size |\n| game_modes     | string | No       | wordle        | Maze game modes     |\n\n## restart_maze\n\nThis event logs when the user restart the maze game mode.\n\n## maze_item_click\n\nThis event logs when the user click on a maze item.\n\n### Parameters\n\n| Parameter name | Type   | Required | Example value | Description     |\n|----------------|--------|----------|---------------|-----------------|\n| item_index     | number | Yes      | 23            | Maze item index |\n\n## maze_item_played\n\nThis event logs when the user play and ends the game of the maze item.\n\n### Parameters\n\n| Parameter name | Type   | Required | Example value | Description                     |\n|----------------|--------|----------|---------------|---------------------------------|\n| correct        | string | Yes      | true          | The user got the answer correct |\n\n## maze_completed\n\nThis event logs when the user completes all the questions the maze game mode.\n\n### Parameters\n\n| Parameter name | Type   | Required | Example value | Description                     |\n|----------------|--------|----------|---------------|---------------------------------|\n| item_size      | number | Yes      | 23            | Number of questions in the maze |\n\n# Comparison Quiz\n\n## comparison_quiz_game_start\n\nThis event logs the comparison quiz game start.\n\n### Parameters\n\n| Parameter name  | Type   | Required | Example value      | Description                          |\n|-----------------|--------|----------|--------------------|--------------------------------------|\n| category        | string | Yes      | country_population | The category of the quiz             |\n| comparison_mode | string | Yes      | higher             | The comparison mode used in the quiz |\n\n## comparison_quiz_game_end\n\nThis event logs the comparison quiz game end.\n\n### Parameters\n\n| Parameter name   | Type   | Required | Example value      | Description                          |\n|------------------|--------|----------|--------------------|--------------------------------------|\n| category         | string | No       | country_population | The category of the quiz             |\n| comparison_mode  | string | No       | higher             | The comparison mode used in the quiz |\n| score            | number | Yes      | 10                 | The score of the quiz                |\n"
  },
  {
    "path": "core/analytics/README.md",
    "content": "# Analytics Module (:core:analytics)\n\nThe Analytics module provides a way to track user interactions with the application.\n\n## Normal Flavor\n\nThe normal flavor of the Analytics module uses the [Firebase Analytics](https://firebase.google.com/docs/analytics) service to track user interactions.\n\n## FOSS Flavor\n\nThe FOSS flavor of the Analytics module don't track any user interaction to the any third party services. Only logs the user interactions to the console.\n"
  },
  {
    "path": "core/analytics/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library.compose)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.core.analytics\"\n}\n\ndependencies {\n    implementation(libs.androidx.compose.runtime)\n\n    normalImplementation(platform(libs.firebase.bom))\n    normalImplementation(libs.firebase.analytics)\n    normalImplementation(libs.firebase.crashlytics)\n    normalImplementation(libs.firebase.perf)\n\n    implementation(projects.model)\n    normalImplementation(projects.core.datastore)\n}\n"
  },
  {
    "path": "core/analytics/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "core/analytics/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "core/analytics/src/foss/kotlin/com/infinitepower/newquiz/core/analytics/FossAnalyticsModule.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\n\n@Module\n@InstallIn(SingletonComponent::class)\nabstract class FossAnalyticsModule {\n    @Binds\n    abstract fun bindAnalyticsHelper(impl: LocalDebugAnalyticsHelper): AnalyticsHelper\n}"
  },
  {
    "path": "core/analytics/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />"
  },
  {
    "path": "core/analytics/src/main/kotlin/com/infinitepower/newquiz/core/analytics/AnalyticsEvent.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.global_event.GameEvent\n\nsealed class AnalyticsEvent(\n    val type: String,\n    val extras: Set<Param<*>> = emptySet()\n) {\n    @Keep\n    data class Param<T>(\n        val key: String,\n        val value: T?\n    )\n\n    /**\n     * Enum class for the different games that can be played.\n     */\n    enum class Game {\n        MULTI_CHOICE_QUIZ,\n        WORDLE,\n        COMPARISON_QUIZ\n    }\n\n    // Core events\n\n    @Keep\n    data class CategoryClicked(\n        val game: Game,\n        val categoryId: String,\n        val otherData: Map<String, Any?> = emptyMap()\n    ) : AnalyticsEvent(\n        type = \"category_clicked\",\n        extras = setOf(\n            Param(\"game\", game.name.lowercase()),\n            Param(\"id\", categoryId)\n        ) + otherData.map { Param(it.key, it.value) }\n    )\n\n    @Keep\n    data class LevelUp(val level: Int) : AnalyticsEvent(\n        type = \"level_up\",\n        extras = setOf(\n            Param(\"level\", level),\n            Param(\"character\", \"global\")\n        )\n    )\n\n    @Keep\n    data class EarnDiamonds(val earned: Int) : AnalyticsEvent(\n        type = \"earn_virtual_currency\",\n        extras = setOf(\n            Param(\"virtual_currency_name\", \"diamonds\"),\n            Param(\"value\", earned)\n        )\n    )\n\n    @Keep\n    data class SpendDiamonds(val amount: Int, val usedFor: String) : AnalyticsEvent(\n        type = \"spend_virtual_currency\",\n        extras = setOf(\n            Param(\"value\", amount),\n            Param(\"virtual_currency_name\", \"diamonds\"),\n            Param(\"item_name\", usedFor)\n        )\n    )\n\n    // Multi choice quiz events\n\n    @Keep\n    data class MultiChoiceGameStart(\n        val questionsSize: Int,\n        val category: String,\n        val difficulty: String?,\n        val mazeItemId: Int? = null\n    ) : AnalyticsEvent(\n        type = \"multi_choice_game_start\",\n        extras = setOf(\n            Param(\"questions_size\", questionsSize),\n            Param(\"category\", category),\n            Param(\"difficulty\", difficulty),\n            Param(\"maze_item_id\", mazeItemId)\n        )\n    )\n\n    @Keep\n    data class MultiChoiceGameEnd(\n        val questionsSize: Int,\n        val correctAnswers: Int,\n        val mazeItemId: Int? = null\n    ) : AnalyticsEvent(\n        type = \"multi_choice_game_end\",\n        extras = setOf(\n            Param(\"questions_size\", questionsSize),\n            Param(\"correct_answers\", correctAnswers),\n            Param(\"maze_item_id\", mazeItemId)\n        )\n    )\n\n    data object MultiChoiceSaveQuestion : AnalyticsEvent(\"multi_choice_save_question\")\n\n    data object MultiChoiceDownloadQuestions : AnalyticsEvent(\"multi_choice_download_questions\")\n\n    @Keep\n    data class MultiChoicePlaySavedQuestions(\n        val questionsSize: Int\n    ) : AnalyticsEvent(\n        type = \"multi_choice_play_saved_questions\",\n        extras = setOf(\n            Param(\"questions_size\", questionsSize)\n        )\n    )\n\n    // Wordle events\n\n    @Keep\n    data class WordleGameStart(\n        val wordLength: Int,\n        val maxRows: Int,\n        val category: String,\n        val mazeItemId: Int? = null\n    ) : AnalyticsEvent(\n        type = \"wordle_game_start\",\n        extras = setOf(\n            Param(\"word_length\", wordLength),\n            Param(\"max_rows\", maxRows),\n            Param(\"category\", category),\n            Param(\"maze_item_id\", mazeItemId)\n        )\n    )\n\n    @Keep\n    data class WordleGameEnd(\n        val wordLength: Int,\n        val maxRows: Int,\n        val lastRow: Int,\n        val lastRowCorrect: Boolean,\n        val category: String,\n        val mazeItemId: Int? = null\n    ) : AnalyticsEvent(\n        type = \"wordle_game_end\",\n        extras = setOf(\n            Param(\"word_length\", wordLength),\n            Param(\"max_rows\", maxRows),\n            Param(\"last_row\", lastRow),\n            Param(\"last_row_correct\", lastRowCorrect),\n            Param(\"category\", category),\n            Param(\"maze_item_id\", mazeItemId)\n        )\n    )\n\n    // Maze events\n\n    @Keep\n    data class CreateMaze(\n        val seed: Int,\n        val questionsSize: Int\n    ) : AnalyticsEvent(\n        type = \"create_maze\",\n        extras = setOf(\n            Param(\"seed\", seed),\n            Param(\"questions_size\", questionsSize)\n        )\n    )\n\n    data object RestartMaze : AnalyticsEvent(\"restart_maze\")\n\n    @Keep\n    data class MazeItemClick(val index: Int) : AnalyticsEvent(\n        type = \"maze_item_click\",\n        extras = setOf(\n            Param(\"index\", index)\n        )\n    )\n\n    @Keep\n    data class MazeItemPlayed(val correct: Boolean) : AnalyticsEvent(\n        type = \"maze_item_played\",\n        extras = setOf(\n            Param(\"correct\", correct)\n        )\n    )\n\n    @Keep\n    data class MazeCompleted(val questionSize: Int) : AnalyticsEvent(\n        type = \"maze_completed\",\n        extras = setOf(\n            Param(\"question_size\", questionSize)\n        )\n    )\n\n    // Comparison Quiz\n\n    @Keep\n    data class ComparisonQuizGameStart(\n        val category: String,\n        val comparisonMode: String,\n    ) : AnalyticsEvent(\n        type = \"comparison_quiz_game_start\",\n        extras = setOf(\n            Param(\"category\", category),\n            Param(\"comparison_mode\", comparisonMode)\n        )\n    )\n\n    @Keep\n    data class ComparisonQuizGameEnd(\n        val category: String?,\n        val comparisonMode: String?,\n        val score: Int\n    ) : AnalyticsEvent(\n        type = \"comparison_quiz_game_end\",\n        extras = setOf(\n            Param(\"category\", category),\n            Param(\"comparison_mode\", comparisonMode),\n            Param(\"score\", score)\n        )\n    )\n\n    // Daily challenge\n    @Keep\n    data class DailyChallengeItemClick(\n        val event: GameEvent\n    ) : AnalyticsEvent(\n        type = \"daily_challenge_item_click\",\n        extras = setOf(\n            Param(\"event\", event.toString())\n        )\n    )\n\n    @Keep\n    data class DailyChallengeItemClaim(\n        val event: GameEvent,\n        val steps: Int\n    ) : AnalyticsEvent(\n        type = \"daily_challenge_item_claim\",\n        extras = setOf(\n            Param(\"event\", event.toString()),\n            Param(\"steps\", steps)\n        )\n    )\n}\n"
  },
  {
    "path": "core/analytics/src/main/kotlin/com/infinitepower/newquiz/core/analytics/AnalyticsHelper.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport kotlinx.coroutines.flow.Flow\n\n/**\n * Interface for logging analytics events.\n */\ninterface AnalyticsHelper {\n    /**\n     * Logs the given [events].\n     *\n     * @param events the events to log.\n     * @see AnalyticsEvent\n     */\n    fun logEvent(vararg events: AnalyticsEvent)\n\n    /**\n     * Sets the user property to the given value.\n     *\n     * @param userProperty the user property to set.\n     * @see UserProperty\n     */\n    fun setUserProperty(userProperty: UserProperty)\n\n    fun setGeneralAnalyticsEnabled(enabled: Boolean)\n\n    fun setCrashlyticsEnabled(enabled: Boolean)\n\n    fun setPerformanceEnabled(enabled: Boolean)\n\n    val showDataAnalyticsConsentDialog: Flow<Boolean>\n\n    suspend fun updateDataConsent(agreed: Boolean)\n}\n"
  },
  {
    "path": "core/analytics/src/main/kotlin/com/infinitepower/newquiz/core/analytics/LocalDebugAnalyticsHelper.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport android.util.Log\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.flowOf\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\nprivate const val TAG = \"LocalAnalyticsHelper\"\n\n@Singleton\nclass LocalDebugAnalyticsHelper @Inject constructor() : AnalyticsHelper {\n    override fun logEvent(vararg events: AnalyticsEvent) {\n        Log.d(TAG, \"Received events: ${events.joinToString()}\")\n    }\n\n    override fun setUserProperty(userProperty: UserProperty) {\n        Log.d(TAG, \"Received user property: $userProperty\")\n    }\n\n    override fun setGeneralAnalyticsEnabled(enabled: Boolean) {\n        Log.d(TAG, \"enableGeneralAnalytics: $enabled\")\n    }\n\n    override fun setCrashlyticsEnabled(enabled: Boolean) {\n        Log.d(TAG, \"enableCrashlytics: $enabled\")\n    }\n\n    override fun setPerformanceEnabled(enabled: Boolean) {\n        Log.d(TAG, \"enablePerformanceMonitoring: $enabled\")\n    }\n\n    // Because this is a local debug analytics helper, we don't want to show the dialog to the user\n    override val showDataAnalyticsConsentDialog: Flow<Boolean> = flowOf(false)\n\n    override suspend fun updateDataConsent(agreed: Boolean) {\n        Log.d(TAG, \"updateDataConsent: $agreed\")\n    }\n}\n"
  },
  {
    "path": "core/analytics/src/main/kotlin/com/infinitepower/newquiz/core/analytics/NoOpAnalyticsHelper.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.emptyFlow\n\n/**\n * Implementation of [AnalyticsHelper] which does nothing. Useful for tests and previews.\n */\nobject NoOpAnalyticsHelper : AnalyticsHelper {\n    override fun logEvent(vararg events: AnalyticsEvent) = Unit\n\n    override fun setUserProperty(userProperty: UserProperty) = Unit\n\n    override fun setGeneralAnalyticsEnabled(enabled: Boolean) = Unit\n\n    override fun setCrashlyticsEnabled(enabled: Boolean) = Unit\n\n    override fun setPerformanceEnabled(enabled: Boolean) = Unit\n\n    override val showDataAnalyticsConsentDialog: Flow<Boolean> = emptyFlow()\n\n    override suspend fun updateDataConsent(agreed: Boolean) = Unit\n}\n"
  },
  {
    "path": "core/analytics/src/main/kotlin/com/infinitepower/newquiz/core/analytics/UiHelpers.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport androidx.compose.runtime.staticCompositionLocalOf\n\n/**\n * A local composition that provides an [AnalyticsHelper] instance.\n */\nval LocalAnalyticsHelper = staticCompositionLocalOf<AnalyticsHelper> {\n    LocalDebugAnalyticsHelper()\n}\n"
  },
  {
    "path": "core/analytics/src/main/kotlin/com/infinitepower/newquiz/core/analytics/UserProperty.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport androidx.annotation.Size\n\nsealed class UserProperty(\n    @Size(min = 1L, max = 24L) val name: String,\n    @Size(max = 36L) val value: String?\n) {\n    data class WordleLanguage(val lang: String) : UserProperty(\n        name = \"wordle_lang\",\n        value = lang\n    )\n\n    data class TranslatorModelDownloaded(val downloaded: Boolean) : UserProperty(\n        name = \"translator_downloaded\",\n        value = downloaded.toString()\n    )\n}\n"
  },
  {
    "path": "core/analytics/src/normal/kotlin/com/infinitepower/newquiz/core/analytics/FirebaseAnalyticsHelper.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport com.google.firebase.analytics.FirebaseAnalytics\nimport com.google.firebase.analytics.ParametersBuilder\nimport com.google.firebase.analytics.logEvent\nimport com.google.firebase.crashlytics.FirebaseCrashlytics\nimport com.google.firebase.perf.FirebasePerformance\nimport com.infinitepower.newquiz.core.datastore.common.DataAnalyticsCommon\nimport com.infinitepower.newquiz.core.datastore.di.DataAnalyticsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.model.DataAnalyticsConsentState\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.map\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass FirebaseAnalyticsHelper @Inject constructor(\n    private val firebaseAnalytics: FirebaseAnalytics,\n    private val firebaseCrashlytics: FirebaseCrashlytics,\n    private val firebasePerformance: FirebasePerformance,\n    @DataAnalyticsDataStoreManager private val dataAnalyticsDataStoreManager: DataStoreManager,\n) : AnalyticsHelper {\n    private fun ParametersBuilder.param(\n        analyticsParam: AnalyticsEvent.Param<*>\n    ) {\n        when (analyticsParam.value) {\n            null -> {}\n            is String -> param(analyticsParam.key, analyticsParam.value)\n            is Long -> param(analyticsParam.key, analyticsParam.value)\n            is Double -> param(analyticsParam.key, analyticsParam.value)\n            is Int -> param(analyticsParam.key, analyticsParam.value.toLong())\n            else -> param(analyticsParam.key, analyticsParam.value.toString())\n        }\n    }\n\n    override fun logEvent(vararg events: AnalyticsEvent) {\n        events.forEach { analyticsEvent ->\n            firebaseAnalytics.logEvent(analyticsEvent.type) {\n                analyticsEvent.extras.forEach { extra ->\n                    param(extra)\n                }\n            }\n        }\n    }\n\n    override fun setUserProperty(userProperty: UserProperty) {\n        firebaseAnalytics.setUserProperty(userProperty.name, userProperty.value)\n    }\n\n    override fun setGeneralAnalyticsEnabled(enabled: Boolean) {\n        firebaseAnalytics.setAnalyticsCollectionEnabled(enabled)\n    }\n\n    override fun setCrashlyticsEnabled(enabled: Boolean) {\n        firebaseCrashlytics.setCrashlyticsCollectionEnabled(enabled)\n    }\n\n    override fun setPerformanceEnabled(enabled: Boolean) {\n        firebasePerformance.isPerformanceCollectionEnabled = enabled\n    }\n\n    override val showDataAnalyticsConsentDialog: Flow<Boolean>\n        get() = dataAnalyticsDataStoreManager\n            .getPreferenceFlow(DataAnalyticsCommon.DataAnalyticsConsent)\n            .map { consentStr ->\n                val consent = DataAnalyticsConsentState.valueOf(consentStr)\n\n                consent == DataAnalyticsConsentState.NONE\n            }\n\n    override suspend fun updateDataConsent(agreed: Boolean) {\n        val consentState = if (agreed) {\n            DataAnalyticsConsentState.AGREED\n        } else {\n            DataAnalyticsConsentState.DISAGREED\n        }\n\n        dataAnalyticsDataStoreManager.editPreference(\n            key = DataAnalyticsCommon.DataAnalyticsConsent.key,\n            newValue = consentState.name\n        )\n\n        // Update the parent data analytics settings\n        dataAnalyticsDataStoreManager.editPreference(\n            key = DataAnalyticsCommon.GloballyAnalyticsCollectionEnabled.key,\n            newValue = agreed\n        )\n\n        // Enable or disable the individual analytics settings,\n        // and update the datastore with the new values\n\n        setGeneralAnalyticsEnabled(agreed)\n        dataAnalyticsDataStoreManager.editPreference(\n            key = DataAnalyticsCommon.GeneralAnalyticsEnabled.key,\n            newValue = agreed\n        )\n\n        setCrashlyticsEnabled(agreed)\n        dataAnalyticsDataStoreManager.editPreference(\n            key = DataAnalyticsCommon.CrashlyticsEnabled.key,\n            newValue = agreed\n        )\n\n        setPerformanceEnabled(agreed)\n        dataAnalyticsDataStoreManager.editPreference(\n            key = DataAnalyticsCommon.PerformanceMonitoringEnabled.key,\n            newValue = agreed\n        )\n    }\n}"
  },
  {
    "path": "core/analytics/src/normal/kotlin/com/infinitepower/newquiz/core/analytics/NormalAnalyticsModule.kt",
    "content": "package com.infinitepower.newquiz.core.analytics\n\nimport com.google.firebase.analytics.FirebaseAnalytics\nimport com.google.firebase.analytics.ktx.analytics\nimport com.google.firebase.crashlytics.FirebaseCrashlytics\nimport com.google.firebase.crashlytics.ktx.crashlytics\nimport com.google.firebase.ktx.Firebase\nimport com.google.firebase.perf.FirebasePerformance\nimport com.google.firebase.perf.ktx.performance\nimport dagger.Binds\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nabstract class NormalAnalyticsModule {\n    @Binds\n    abstract fun bindAnalyticsHelper(impl: FirebaseAnalyticsHelper): AnalyticsHelper\n\n    companion object {\n        @Provides\n        @Singleton\n        fun provideFirebaseAnalytics(): FirebaseAnalytics = Firebase.analytics\n\n        @Provides\n        @Singleton\n        fun provideFirebaseCrashlytics(): FirebaseCrashlytics = Firebase.crashlytics\n\n        @Provides\n        @Singleton\n        fun provideFirebasePerformance(): FirebasePerformance = Firebase.performance\n    }\n}"
  },
  {
    "path": "core/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library.compose)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.core\"\n\n    lint {\n        disable += \"MissingTranslation\"\n    }\n}\n\ndependencies {\n    androidTestImplementation(libs.androidx.test.runner)\n    androidTestImplementation(libs.androidx.test.rules)\n    androidTestImplementation(libs.androidx.compose.ui.test)\n    debugApi(libs.androidx.tracing.ktx)\n\n    implementation(libs.androidx.core.ktx)\n\n    implementation(libs.androidx.compose.ui)\n    implementation(libs.androidx.compose.ui.tooling)\n    implementation(libs.androidx.compose.ui.tooling.preview)\n    implementation(libs.androidx.compose.material.iconsExtended)\n    implementation(libs.androidx.compose.material3)\n    implementation(libs.androidx.constraintlayout.compose)\n    debugImplementation(libs.androidx.compose.ui.testManifest)\n\n    implementation(libs.hilt.navigationCompose)\n    implementation(libs.hilt.ext.work)\n    ksp(libs.hilt.ext.compiler)\n\n    implementation(libs.androidx.work.ktx)\n\n    implementation(libs.google.material)\n\n    normalImplementation(platform(libs.firebase.bom))\n    normalImplementation(libs.firebase.analytics)\n\n    implementation(libs.lottie.compose)\n\n    implementation(libs.kotlinx.datetime)\n    testImplementation(libs.kotlinx.coroutines.test)\n\n    implementation(libs.coil.kt.compose)\n    implementation(libs.coil.kt.svg)\n\n    //implementation(\"androidx.palette:palette-ktx:_\")\n\n    implementation(libs.ktor.client.core)\n    implementation(libs.ktor.client.okhttp)\n    implementation(libs.ktor.client.serialization)\n\n    // Modules\n    implementation(projects.model)\n    testImplementation(projects.core.testing)\n    androidTestImplementation(projects.core.testing)\n}\n"
  },
  {
    "path": "core/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "core/database/.gitignore",
    "content": "/build"
  },
  {
    "path": "core/database/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library)\n    alias(libs.plugins.newquiz.android.room)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.core.database\"\n}\n\ndependencies {\n    implementation(libs.kotlinx.datetime)\n\n    implementation(projects.model)\n\n    androidTestImplementation(projects.core.testing)\n}\n"
  },
  {
    "path": "core/database/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "core/database/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "core/database/schemas/com.infinitepower.newquiz.core.database.AppDatabase/1.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 1,\n    \"identityHash\": \"2b5755fa1bcc6adc57c9ab82bd83f2e3\",\n    \"entities\": [\n      {\n        \"tableName\": \"saved_multi_choice_questions\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `description` TEXT NOT NULL, `image_url` TEXT, `answers` TEXT NOT NULL, `lang` TEXT NOT NULL, `category` TEXT NOT NULL, `correct_ans` INTEGER NOT NULL, `type` TEXT NOT NULL, `difficulty` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"description\",\n            \"columnName\": \"description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"imageUrl\",\n            \"columnName\": \"image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"answers\",\n            \"columnName\": \"answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"lang\",\n            \"columnName\": \"lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"category\",\n            \"columnName\": \"category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAns\",\n            \"columnName\": \"correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"wordle_daily_calendar\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` TEXT NOT NULL, `state` TEXT NOT NULL, `wordSize` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"date\",\n            \"columnName\": \"date\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"state\",\n            \"columnName\": \"state\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"wordSize\",\n            \"columnName\": \"wordSize\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"mazeItems\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `difficulty` TEXT NOT NULL, `played` INTEGER NOT NULL, `type` TEXT NOT NULL, `mazeSeed` INTEGER NOT NULL, `maze_question_id` INTEGER, `maze_question_description` TEXT, `maze_question_image_url` TEXT, `maze_question_answers` TEXT, `maze_question_lang` TEXT, `maze_question_category` TEXT, `maze_question_correct_ans` INTEGER, `maze_question_type` TEXT, `maze_question_difficulty` TEXT, `maze_wordle_wordleWord` TEXT, `maze_wordle_wordleQuizType` TEXT, `maze_wordle_textHelper` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"played\",\n            \"columnName\": \"played\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"mazeSeed\",\n            \"columnName\": \"mazeSeed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.id\",\n            \"columnName\": \"maze_question_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.description\",\n            \"columnName\": \"maze_question_description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.imageUrl\",\n            \"columnName\": \"maze_question_image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.answers\",\n            \"columnName\": \"maze_question_answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.lang\",\n            \"columnName\": \"maze_question_lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.category\",\n            \"columnName\": \"maze_question_category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.correctAns\",\n            \"columnName\": \"maze_question_correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.type\",\n            \"columnName\": \"maze_question_type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.difficulty\",\n            \"columnName\": \"maze_question_difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleWord\",\n            \"columnName\": \"maze_wordle_wordleWord\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleQuizType\",\n            \"columnName\": \"maze_wordle_wordleQuizType\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.textHelper\",\n            \"columnName\": \"maze_wordle_textHelper\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2b5755fa1bcc6adc57c9ab82bd83f2e3')\"\n    ]\n  }\n}"
  },
  {
    "path": "core/database/schemas/com.infinitepower.newquiz.core.database.AppDatabase/2.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 2,\n    \"identityHash\": \"fcd9b57e8faa1a7b07c05b842276b5cf\",\n    \"entities\": [\n      {\n        \"tableName\": \"saved_multi_choice_questions\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `description` TEXT NOT NULL, `image_url` TEXT, `answers` TEXT NOT NULL, `lang` TEXT NOT NULL, `category` TEXT NOT NULL, `correct_ans` INTEGER NOT NULL, `type` TEXT NOT NULL, `difficulty` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"description\",\n            \"columnName\": \"description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"imageUrl\",\n            \"columnName\": \"image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"answers\",\n            \"columnName\": \"answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"lang\",\n            \"columnName\": \"lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"category\",\n            \"columnName\": \"category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAns\",\n            \"columnName\": \"correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"wordle_daily_calendar\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` TEXT NOT NULL, `state` TEXT NOT NULL, `wordSize` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"date\",\n            \"columnName\": \"date\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"state\",\n            \"columnName\": \"state\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"wordSize\",\n            \"columnName\": \"wordSize\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"mazeItems\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `difficulty` TEXT NOT NULL, `played` INTEGER NOT NULL, `type` TEXT NOT NULL, `mazeSeed` INTEGER NOT NULL, `maze_question_id` INTEGER, `maze_question_description` TEXT, `maze_question_image_url` TEXT, `maze_question_answers` TEXT, `maze_question_lang` TEXT, `maze_question_category` TEXT, `maze_question_correct_ans` INTEGER, `maze_question_type` TEXT, `maze_question_difficulty` TEXT, `maze_wordle_wordleWord` TEXT, `maze_wordle_wordleQuizType` TEXT, `maze_wordle_textHelper` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"played\",\n            \"columnName\": \"played\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"mazeSeed\",\n            \"columnName\": \"mazeSeed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.id\",\n            \"columnName\": \"maze_question_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.description\",\n            \"columnName\": \"maze_question_description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.imageUrl\",\n            \"columnName\": \"maze_question_image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.answers\",\n            \"columnName\": \"maze_question_answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.lang\",\n            \"columnName\": \"maze_question_lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.category\",\n            \"columnName\": \"maze_question_category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.correctAns\",\n            \"columnName\": \"maze_question_correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.type\",\n            \"columnName\": \"maze_question_type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.difficulty\",\n            \"columnName\": \"maze_question_difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleWord\",\n            \"columnName\": \"maze_wordle_wordleWord\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleQuizType\",\n            \"columnName\": \"maze_wordle_wordleQuizType\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.textHelper\",\n            \"columnName\": \"maze_wordle_textHelper\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"daily_challenge_tasks\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `diamondsReward` INTEGER NOT NULL, `experienceReward` INTEGER NOT NULL, `isClaimed` INTEGER NOT NULL, `currentValue` INTEGER NOT NULL, `maxValue` INTEGER NOT NULL, `type` TEXT NOT NULL, `startDate` INTEGER NOT NULL, `endDate` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"diamondsReward\",\n            \"columnName\": \"diamondsReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"experienceReward\",\n            \"columnName\": \"experienceReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"isClaimed\",\n            \"columnName\": \"isClaimed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"currentValue\",\n            \"columnName\": \"currentValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxValue\",\n            \"columnName\": \"maxValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"startDate\",\n            \"columnName\": \"startDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endDate\",\n            \"columnName\": \"endDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fcd9b57e8faa1a7b07c05b842276b5cf')\"\n    ]\n  }\n}"
  },
  {
    "path": "core/database/schemas/com.infinitepower.newquiz.core.database.AppDatabase/3.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 3,\n    \"identityHash\": \"de2a8eef760db8b584b478e1043749db\",\n    \"entities\": [\n      {\n        \"tableName\": \"saved_multi_choice_questions\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `description` TEXT NOT NULL, `image_url` TEXT, `answers` TEXT NOT NULL, `lang` TEXT NOT NULL, `category` TEXT NOT NULL, `correct_ans` INTEGER NOT NULL, `type` TEXT NOT NULL, `difficulty` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"description\",\n            \"columnName\": \"description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"imageUrl\",\n            \"columnName\": \"image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"answers\",\n            \"columnName\": \"answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"lang\",\n            \"columnName\": \"lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"category\",\n            \"columnName\": \"category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAns\",\n            \"columnName\": \"correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"mazeItems\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `difficulty` TEXT NOT NULL, `played` INTEGER NOT NULL, `type` TEXT NOT NULL, `mazeSeed` INTEGER NOT NULL, `maze_question_id` INTEGER, `maze_question_description` TEXT, `maze_question_image_url` TEXT, `maze_question_answers` TEXT, `maze_question_lang` TEXT, `maze_question_category` TEXT, `maze_question_correct_ans` INTEGER, `maze_question_type` TEXT, `maze_question_difficulty` TEXT, `maze_wordle_wordleWord` TEXT, `maze_wordle_wordleQuizType` TEXT, `maze_wordle_textHelper` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"played\",\n            \"columnName\": \"played\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"mazeSeed\",\n            \"columnName\": \"mazeSeed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.id\",\n            \"columnName\": \"maze_question_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.description\",\n            \"columnName\": \"maze_question_description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.imageUrl\",\n            \"columnName\": \"maze_question_image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.answers\",\n            \"columnName\": \"maze_question_answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.lang\",\n            \"columnName\": \"maze_question_lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.category\",\n            \"columnName\": \"maze_question_category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.correctAns\",\n            \"columnName\": \"maze_question_correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.type\",\n            \"columnName\": \"maze_question_type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.difficulty\",\n            \"columnName\": \"maze_question_difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleWord\",\n            \"columnName\": \"maze_wordle_wordleWord\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleQuizType\",\n            \"columnName\": \"maze_wordle_wordleQuizType\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.textHelper\",\n            \"columnName\": \"maze_wordle_textHelper\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"daily_challenge_tasks\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `diamondsReward` INTEGER NOT NULL, `experienceReward` INTEGER NOT NULL, `isClaimed` INTEGER NOT NULL, `currentValue` INTEGER NOT NULL, `maxValue` INTEGER NOT NULL, `type` TEXT NOT NULL, `startDate` INTEGER NOT NULL, `endDate` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"diamondsReward\",\n            \"columnName\": \"diamondsReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"experienceReward\",\n            \"columnName\": \"experienceReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"isClaimed\",\n            \"columnName\": \"isClaimed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"currentValue\",\n            \"columnName\": \"currentValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxValue\",\n            \"columnName\": \"maxValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"startDate\",\n            \"columnName\": \"startDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endDate\",\n            \"columnName\": \"endDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'de2a8eef760db8b584b478e1043749db')\"\n    ]\n  }\n}"
  },
  {
    "path": "core/database/schemas/com.infinitepower.newquiz.core.database.AppDatabase/4.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 4,\n    \"identityHash\": \"ca501a93f18f0b24e9772858b9dd5ee8\",\n    \"entities\": [\n      {\n        \"tableName\": \"saved_multi_choice_questions\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `description` TEXT NOT NULL, `image_url` TEXT, `answers` TEXT NOT NULL, `lang` TEXT NOT NULL, `category` TEXT NOT NULL, `correct_ans` INTEGER NOT NULL, `type` TEXT NOT NULL, `difficulty` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"description\",\n            \"columnName\": \"description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"imageUrl\",\n            \"columnName\": \"image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"answers\",\n            \"columnName\": \"answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"lang\",\n            \"columnName\": \"lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"category\",\n            \"columnName\": \"category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAns\",\n            \"columnName\": \"correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"mazeItems\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `difficulty` TEXT NOT NULL, `played` INTEGER NOT NULL, `type` TEXT NOT NULL, `mazeSeed` INTEGER NOT NULL, `maze_question_id` INTEGER, `maze_question_description` TEXT, `maze_question_image_url` TEXT, `maze_question_answers` TEXT, `maze_question_lang` TEXT, `maze_question_category` TEXT, `maze_question_correct_ans` INTEGER, `maze_question_type` TEXT, `maze_question_difficulty` TEXT, `maze_wordle_wordleWord` TEXT, `maze_wordle_wordleQuizType` TEXT, `maze_wordle_textHelper` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"played\",\n            \"columnName\": \"played\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"mazeSeed\",\n            \"columnName\": \"mazeSeed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.id\",\n            \"columnName\": \"maze_question_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.description\",\n            \"columnName\": \"maze_question_description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.imageUrl\",\n            \"columnName\": \"maze_question_image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.answers\",\n            \"columnName\": \"maze_question_answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.lang\",\n            \"columnName\": \"maze_question_lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.category\",\n            \"columnName\": \"maze_question_category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.correctAns\",\n            \"columnName\": \"maze_question_correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.type\",\n            \"columnName\": \"maze_question_type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.difficulty\",\n            \"columnName\": \"maze_question_difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleWord\",\n            \"columnName\": \"maze_wordle_wordleWord\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleQuizType\",\n            \"columnName\": \"maze_wordle_wordleQuizType\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.textHelper\",\n            \"columnName\": \"maze_wordle_textHelper\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"daily_challenge_tasks\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `diamondsReward` INTEGER NOT NULL, `experienceReward` INTEGER NOT NULL, `isClaimed` INTEGER NOT NULL, `currentValue` INTEGER NOT NULL, `maxValue` INTEGER NOT NULL, `type` TEXT NOT NULL, `startDate` INTEGER NOT NULL, `endDate` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"diamondsReward\",\n            \"columnName\": \"diamondsReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"experienceReward\",\n            \"columnName\": \"experienceReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"isClaimed\",\n            \"columnName\": \"isClaimed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"currentValue\",\n            \"columnName\": \"currentValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxValue\",\n            \"columnName\": \"maxValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"startDate\",\n            \"columnName\": \"startDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endDate\",\n            \"columnName\": \"endDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"comparison_quiz_highest_position\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` TEXT NOT NULL, `highestPosition` INTEGER NOT NULL, PRIMARY KEY(`categoryId`))\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"categoryId\",\n            \"columnName\": \"categoryId\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"highestPosition\",\n            \"columnName\": \"highestPosition\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": false,\n          \"columnNames\": [\n            \"categoryId\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ca501a93f18f0b24e9772858b9dd5ee8')\"\n    ]\n  }\n}"
  },
  {
    "path": "core/database/schemas/com.infinitepower.newquiz.core.database.AppDatabase/5.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 5,\n    \"identityHash\": \"4e64a492cda5cf6ef8b21c3c9599482a\",\n    \"entities\": [\n      {\n        \"tableName\": \"saved_multi_choice_questions\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `description` TEXT NOT NULL, `image_url` TEXT, `answers` TEXT NOT NULL, `lang` TEXT NOT NULL, `category` TEXT NOT NULL, `correct_ans` INTEGER NOT NULL, `type` TEXT NOT NULL, `difficulty` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"description\",\n            \"columnName\": \"description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"imageUrl\",\n            \"columnName\": \"image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"answers\",\n            \"columnName\": \"answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"lang\",\n            \"columnName\": \"lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"category\",\n            \"columnName\": \"category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAns\",\n            \"columnName\": \"correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"mazeItems\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `difficulty` TEXT NOT NULL, `played` INTEGER NOT NULL, `type` TEXT NOT NULL, `mazeSeed` INTEGER NOT NULL, `maze_question_id` INTEGER, `maze_question_description` TEXT, `maze_question_image_url` TEXT, `maze_question_answers` TEXT, `maze_question_lang` TEXT, `maze_question_category` TEXT, `maze_question_correct_ans` INTEGER, `maze_question_type` TEXT, `maze_question_difficulty` TEXT, `maze_wordle_wordleWord` TEXT, `maze_wordle_wordleQuizType` TEXT, `maze_wordle_textHelper` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"played\",\n            \"columnName\": \"played\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"mazeSeed\",\n            \"columnName\": \"mazeSeed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.id\",\n            \"columnName\": \"maze_question_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.description\",\n            \"columnName\": \"maze_question_description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.imageUrl\",\n            \"columnName\": \"maze_question_image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.answers\",\n            \"columnName\": \"maze_question_answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.lang\",\n            \"columnName\": \"maze_question_lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.category\",\n            \"columnName\": \"maze_question_category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.correctAns\",\n            \"columnName\": \"maze_question_correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.type\",\n            \"columnName\": \"maze_question_type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.difficulty\",\n            \"columnName\": \"maze_question_difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleWord\",\n            \"columnName\": \"maze_wordle_wordleWord\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleQuizType\",\n            \"columnName\": \"maze_wordle_wordleQuizType\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.textHelper\",\n            \"columnName\": \"maze_wordle_textHelper\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"daily_challenge_tasks\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `diamondsReward` INTEGER NOT NULL, `experienceReward` INTEGER NOT NULL, `isClaimed` INTEGER NOT NULL, `currentValue` INTEGER NOT NULL, `maxValue` INTEGER NOT NULL, `type` TEXT NOT NULL, `startDate` INTEGER NOT NULL, `endDate` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"diamondsReward\",\n            \"columnName\": \"diamondsReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"experienceReward\",\n            \"columnName\": \"experienceReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"isClaimed\",\n            \"columnName\": \"isClaimed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"currentValue\",\n            \"columnName\": \"currentValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxValue\",\n            \"columnName\": \"maxValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"startDate\",\n            \"columnName\": \"startDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endDate\",\n            \"columnName\": \"endDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"comparison_quiz_highest_position\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` TEXT NOT NULL, `highestPosition` INTEGER NOT NULL, PRIMARY KEY(`categoryId`))\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"categoryId\",\n            \"columnName\": \"categoryId\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"highestPosition\",\n            \"columnName\": \"highestPosition\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": false,\n          \"columnNames\": [\n            \"categoryId\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"multi_choice_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `correct_answers` INTEGER NOT NULL, `skipped_questions` INTEGER NOT NULL, `question_count` INTEGER NOT NULL, `average_answer_time` REAL NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAnswers\",\n            \"columnName\": \"correct_answers\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"skippedQuestions\",\n            \"columnName\": \"skipped_questions\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"questionCount\",\n            \"columnName\": \"question_count\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"averageAnswerTime\",\n            \"columnName\": \"average_answer_time\",\n            \"affinity\": \"REAL\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"wordle_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL, `word_length` INTEGER NOT NULL, `rows_used` INTEGER NOT NULL, `max_rows` INTEGER NOT NULL, `category_id` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"wordLength\",\n            \"columnName\": \"word_length\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"rowsUsed\",\n            \"columnName\": \"rows_used\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxRows\",\n            \"columnName\": \"max_rows\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"categoryId\",\n            \"columnName\": \"category_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"comparison_quiz_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL, `category_id` TEXT NOT NULL, `comparison_mode` TEXT NOT NULL, `end_position` INTEGER NOT NULL, `highest_position` INTEGER NOT NULL, `skipped_answers` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"categoryId\",\n            \"columnName\": \"category_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"comparisonMode\",\n            \"columnName\": \"comparison_mode\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endPosition\",\n            \"columnName\": \"end_position\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"highestPosition\",\n            \"columnName\": \"highest_position\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"skippedAnswers\",\n            \"columnName\": \"skipped_answers\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4e64a492cda5cf6ef8b21c3c9599482a')\"\n    ]\n  }\n}"
  },
  {
    "path": "core/database/schemas/com.infinitepower.newquiz.core.database.AppDatabase/6.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 6,\n    \"identityHash\": \"3a23ffe1cbbd7ba6cecdcd8e2261c827\",\n    \"entities\": [\n      {\n        \"tableName\": \"saved_multi_choice_questions\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `description` TEXT NOT NULL, `image_url` TEXT, `answers` TEXT NOT NULL, `lang` TEXT NOT NULL, `category` TEXT NOT NULL, `correct_ans` INTEGER NOT NULL, `type` TEXT NOT NULL, `difficulty` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"description\",\n            \"columnName\": \"description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"imageUrl\",\n            \"columnName\": \"image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"answers\",\n            \"columnName\": \"answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"lang\",\n            \"columnName\": \"lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"category\",\n            \"columnName\": \"category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAns\",\n            \"columnName\": \"correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"mazeItems\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `difficulty` TEXT NOT NULL, `played` INTEGER NOT NULL, `type` TEXT NOT NULL, `mazeSeed` INTEGER NOT NULL, `maze_question_id` INTEGER, `maze_question_description` TEXT, `maze_question_image_url` TEXT, `maze_question_answers` TEXT, `maze_question_lang` TEXT, `maze_question_category` TEXT, `maze_question_correct_ans` INTEGER, `maze_question_type` TEXT, `maze_question_difficulty` TEXT, `maze_wordle_wordleWord` TEXT, `maze_wordle_wordleQuizType` TEXT, `maze_wordle_textHelper` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"played\",\n            \"columnName\": \"played\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"mazeSeed\",\n            \"columnName\": \"mazeSeed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.id\",\n            \"columnName\": \"maze_question_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.description\",\n            \"columnName\": \"maze_question_description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.imageUrl\",\n            \"columnName\": \"maze_question_image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.answers\",\n            \"columnName\": \"maze_question_answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.lang\",\n            \"columnName\": \"maze_question_lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.category\",\n            \"columnName\": \"maze_question_category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.correctAns\",\n            \"columnName\": \"maze_question_correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.type\",\n            \"columnName\": \"maze_question_type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.difficulty\",\n            \"columnName\": \"maze_question_difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleWord\",\n            \"columnName\": \"maze_wordle_wordleWord\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleQuizType\",\n            \"columnName\": \"maze_wordle_wordleQuizType\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.textHelper\",\n            \"columnName\": \"maze_wordle_textHelper\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"daily_challenge_tasks\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `diamondsReward` INTEGER NOT NULL, `experienceReward` INTEGER NOT NULL, `isClaimed` INTEGER NOT NULL, `currentValue` INTEGER NOT NULL, `maxValue` INTEGER NOT NULL, `type` TEXT NOT NULL, `startDate` INTEGER NOT NULL, `endDate` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"diamondsReward\",\n            \"columnName\": \"diamondsReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"experienceReward\",\n            \"columnName\": \"experienceReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"isClaimed\",\n            \"columnName\": \"isClaimed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"currentValue\",\n            \"columnName\": \"currentValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxValue\",\n            \"columnName\": \"maxValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"startDate\",\n            \"columnName\": \"startDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endDate\",\n            \"columnName\": \"endDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"multi_choice_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `correct_answers` INTEGER NOT NULL, `skipped_questions` INTEGER NOT NULL, `question_count` INTEGER NOT NULL, `average_answer_time` REAL NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAnswers\",\n            \"columnName\": \"correct_answers\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"skippedQuestions\",\n            \"columnName\": \"skipped_questions\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"questionCount\",\n            \"columnName\": \"question_count\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"averageAnswerTime\",\n            \"columnName\": \"average_answer_time\",\n            \"affinity\": \"REAL\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"wordle_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL, `word_length` INTEGER NOT NULL, `rows_used` INTEGER NOT NULL, `max_rows` INTEGER NOT NULL, `category_id` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"wordLength\",\n            \"columnName\": \"word_length\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"rowsUsed\",\n            \"columnName\": \"rows_used\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxRows\",\n            \"columnName\": \"max_rows\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"categoryId\",\n            \"columnName\": \"category_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"comparison_quiz_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL, `category_id` TEXT NOT NULL, `comparison_mode` TEXT NOT NULL, `end_position` INTEGER NOT NULL, `skipped_answers` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"categoryId\",\n            \"columnName\": \"category_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"comparisonMode\",\n            \"columnName\": \"comparison_mode\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endPosition\",\n            \"columnName\": \"end_position\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"skippedAnswers\",\n            \"columnName\": \"skipped_answers\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3a23ffe1cbbd7ba6cecdcd8e2261c827')\"\n    ]\n  }\n}"
  },
  {
    "path": "core/database/schemas/com.infinitepower.newquiz.core.database.AppDatabase/7.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 7,\n    \"identityHash\": \"d699dae423a5b36ad66302c323af52db\",\n    \"entities\": [\n      {\n        \"tableName\": \"saved_multi_choice_questions\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `description` TEXT NOT NULL, `image_url` TEXT, `answers` TEXT NOT NULL, `lang` TEXT NOT NULL, `category` TEXT NOT NULL, `correct_ans` INTEGER NOT NULL, `type` TEXT NOT NULL, `difficulty` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"description\",\n            \"columnName\": \"description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"imageUrl\",\n            \"columnName\": \"image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"answers\",\n            \"columnName\": \"answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"lang\",\n            \"columnName\": \"lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"category\",\n            \"columnName\": \"category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAns\",\n            \"columnName\": \"correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"mazeItems\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `difficulty` TEXT NOT NULL, `played` INTEGER NOT NULL, `type` TEXT NOT NULL, `mazeSeed` INTEGER NOT NULL, `maze_question_id` INTEGER, `maze_question_description` TEXT, `maze_question_image_url` TEXT, `maze_question_answers` TEXT, `maze_question_lang` TEXT, `maze_question_category` TEXT, `maze_question_correct_ans` INTEGER, `maze_question_type` TEXT, `maze_question_difficulty` TEXT, `maze_wordle_wordleWord` TEXT, `maze_wordle_wordleQuizType` TEXT, `maze_wordle_textHelper` TEXT, `comparison_quiz_category` TEXT, `comparison_quiz_comparisonMode` TEXT, `comparison_quiz_first_question_title` TEXT, `comparison_quiz_first_question_value` REAL, `comparison_quiz_first_question_imgUrl` TEXT, `comparison_quiz_second_question_title` TEXT, `comparison_quiz_second_question_value` REAL, `comparison_quiz_second_question_imgUrl` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"difficulty\",\n            \"columnName\": \"difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"played\",\n            \"columnName\": \"played\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"mazeSeed\",\n            \"columnName\": \"mazeSeed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.id\",\n            \"columnName\": \"maze_question_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.description\",\n            \"columnName\": \"maze_question_description\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.imageUrl\",\n            \"columnName\": \"maze_question_image_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.answers\",\n            \"columnName\": \"maze_question_answers\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.lang\",\n            \"columnName\": \"maze_question_lang\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.category\",\n            \"columnName\": \"maze_question_category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.correctAns\",\n            \"columnName\": \"maze_question_correct_ans\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.type\",\n            \"columnName\": \"maze_question_type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"multiChoiceQuestion.difficulty\",\n            \"columnName\": \"maze_question_difficulty\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleWord\",\n            \"columnName\": \"maze_wordle_wordleWord\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.wordleQuizType\",\n            \"columnName\": \"maze_wordle_wordleQuizType\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"wordleItem.textHelper\",\n            \"columnName\": \"maze_wordle_textHelper\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"comparisonQuizQuestion.category\",\n            \"columnName\": \"comparison_quiz_category\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"comparisonQuizQuestion.comparisonMode\",\n            \"columnName\": \"comparison_quiz_comparisonMode\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"comparisonQuizQuestion.firstQuestion.title\",\n            \"columnName\": \"comparison_quiz_first_question_title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"comparisonQuizQuestion.firstQuestion.value\",\n            \"columnName\": \"comparison_quiz_first_question_value\",\n            \"affinity\": \"REAL\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"comparisonQuizQuestion.firstQuestion.imgUrl\",\n            \"columnName\": \"comparison_quiz_first_question_imgUrl\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"comparisonQuizQuestion.secondQuestion.title\",\n            \"columnName\": \"comparison_quiz_second_question_title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"comparisonQuizQuestion.secondQuestion.value\",\n            \"columnName\": \"comparison_quiz_second_question_value\",\n            \"affinity\": \"REAL\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"comparisonQuizQuestion.secondQuestion.imgUrl\",\n            \"columnName\": \"comparison_quiz_second_question_imgUrl\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"daily_challenge_tasks\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `diamondsReward` INTEGER NOT NULL, `experienceReward` INTEGER NOT NULL, `isClaimed` INTEGER NOT NULL, `currentValue` INTEGER NOT NULL, `maxValue` INTEGER NOT NULL, `type` TEXT NOT NULL, `startDate` INTEGER NOT NULL, `endDate` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"diamondsReward\",\n            \"columnName\": \"diamondsReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"experienceReward\",\n            \"columnName\": \"experienceReward\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"isClaimed\",\n            \"columnName\": \"isClaimed\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"currentValue\",\n            \"columnName\": \"currentValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxValue\",\n            \"columnName\": \"maxValue\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"startDate\",\n            \"columnName\": \"startDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endDate\",\n            \"columnName\": \"endDate\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"multi_choice_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `correct_answers` INTEGER NOT NULL, `skipped_questions` INTEGER NOT NULL, `question_count` INTEGER NOT NULL, `average_answer_time` REAL NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"correctAnswers\",\n            \"columnName\": \"correct_answers\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"skippedQuestions\",\n            \"columnName\": \"skipped_questions\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"questionCount\",\n            \"columnName\": \"question_count\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"averageAnswerTime\",\n            \"columnName\": \"average_answer_time\",\n            \"affinity\": \"REAL\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"wordle_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL, `word_length` INTEGER NOT NULL, `rows_used` INTEGER NOT NULL, `max_rows` INTEGER NOT NULL, `category_id` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"wordLength\",\n            \"columnName\": \"word_length\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"rowsUsed\",\n            \"columnName\": \"rows_used\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"maxRows\",\n            \"columnName\": \"max_rows\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"categoryId\",\n            \"columnName\": \"category_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"comparison_quiz_game_results\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`game_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `earned_xp` INTEGER NOT NULL, `played_at` INTEGER NOT NULL, `category_id` TEXT NOT NULL, `comparison_mode` TEXT NOT NULL, `end_position` INTEGER NOT NULL, `skipped_answers` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"gameId\",\n            \"columnName\": \"game_id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"earnedXp\",\n            \"columnName\": \"earned_xp\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"playedAt\",\n            \"columnName\": \"played_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"categoryId\",\n            \"columnName\": \"category_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"comparisonMode\",\n            \"columnName\": \"comparison_mode\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"endPosition\",\n            \"columnName\": \"end_position\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"skippedAnswers\",\n            \"columnName\": \"skipped_answers\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"autoGenerate\": true,\n          \"columnNames\": [\n            \"game_id\"\n          ]\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd699dae423a5b36ad66302c323af52db')\"\n    ]\n  }\n}"
  },
  {
    "path": "core/database/src/androidTest/AndroidManifest.xml",
    "content": "<manifest\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <application>\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            tools:node=\"remove\" />\n    </application>\n</manifest>"
  },
  {
    "path": "core/database/src/androidTest/kotlin/com/infinitepower/newquiz/core/database/dao/DailyChallengeDaoTest.kt",
    "content": "package com.infinitepower.newquiz.core.database.dao\n\nimport android.content.Context\nimport androidx.room.Room\nimport androidx.test.core.app.ApplicationProvider\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport app.cash.turbine.test\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.AppDatabase\nimport com.infinitepower.newquiz.core.database.model.DailyChallengeTaskEntity\nimport com.infinitepower.newquiz.core.testing.data.fake.FakeData\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.datetime.Clock\nimport org.junit.After\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport java.io.IOException\nimport kotlin.time.Duration.Companion.days\n\n@RunWith(AndroidJUnit4::class)\ninternal class DailyChallengeDaoTest {\n    private lateinit var dailyChallengeDao: DailyChallengeDao\n    private lateinit var db: AppDatabase\n\n    @Before\n    fun setup() {\n        val context = ApplicationProvider.getApplicationContext<Context>()\n        db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()\n        dailyChallengeDao = db.dailyChallengeDao()\n    }\n\n    @After\n    @Throws(IOException::class)\n    fun closeDb() {\n        db.close()\n    }\n\n    @Test\n    fun getAllTasksFlow_returnsFlowOfAllTasks() = runTest {\n        val tasks = FakeData.generateTasks().map(DailyChallengeTask::toEntity)\n        dailyChallengeDao.insertAll(tasks.map { it.copy(id = 0) })\n\n        dailyChallengeDao.getAllTasksFlow().test {\n            val emittedTasks = awaitItem()\n            assertThat(emittedTasks).containsExactlyElementsIn(tasks)\n        }\n    }\n\n    @Test\n    fun getTasksForDateRange_returnsTasksWithinDateRange() = runTest {\n        val now = Clock.System.now()\n        val tasks = FakeData\n            .generateTasksWithOffset(instant = now)\n            .map(DailyChallengeTask::toEntity)\n\n        dailyChallengeDao.insertAll(tasks.map { it.copy(id = 0) })\n\n        val tasksForDateRange = dailyChallengeDao.getAvailableTasks(now.toEpochMilliseconds())\n\n        tasksForDateRange.forEach {\n            assertThat(it.startDate).isAtMost(now.toEpochMilliseconds())\n            assertThat(it.endDate).isAtLeast(now.toEpochMilliseconds())\n        }\n    }\n\n    @Test\n    fun tasksAreAvailable_returnsTrueIfTasksAreAvailable() = runTest {\n        val now = Clock.System.now()\n        val tasks = FakeData\n            .generateTasksWithOffset(instant = now)\n            .map(DailyChallengeTask::toEntity)\n\n        dailyChallengeDao.insertAll(tasks.map { it.copy(id = 0) })\n\n        val areTasksAvailable = dailyChallengeDao.tasksAreAvailable(now.toEpochMilliseconds())\n\n        assertThat(areTasksAvailable).isTrue()\n    }\n\n    @Test\n    fun tasksAreAvailable_returnsFalseIfTasksAreNotAvailable() = runTest {\n        val now = Clock.System.now()\n        val tasks = FakeData\n            .generateTasksWithOffset(instant = now)\n            .map(DailyChallengeTask::toEntity)\n\n        dailyChallengeDao.insertAll(tasks.map { it.copy(id = 0) })\n\n        val areTasksAvailable = dailyChallengeDao.tasksAreAvailable(\n            now.plus(10.days).toEpochMilliseconds()\n        )\n\n        assertThat(areTasksAvailable).isFalse()\n    }\n}\n\nprivate fun DailyChallengeTask.toEntity(): DailyChallengeTaskEntity = DailyChallengeTaskEntity(\n    id = id,\n    diamondsReward = diamondsReward.toInt(),\n    experienceReward = experienceReward.toInt(),\n    isClaimed = isClaimed,\n    currentValue = currentValue.toInt(),\n    maxValue = maxValue.toInt(),\n    type = event.key,\n    startDate = dateRange.start.toEpochMilliseconds(),\n    endDate = dateRange.endInclusive.toEpochMilliseconds()\n)\n"
  },
  {
    "path": "core/database/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/AppDatabase.kt",
    "content": "package com.infinitepower.newquiz.core.database\n\nimport android.annotation.SuppressLint\nimport androidx.room.AutoMigration\nimport androidx.room.Database\nimport androidx.room.DeleteColumn\nimport androidx.room.DeleteTable\nimport androidx.room.RoomDatabase\nimport androidx.room.TypeConverters\nimport androidx.room.migration.AutoMigrationSpec\nimport com.infinitepower.newquiz.core.database.dao.DailyChallengeDao\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.database.dao.MazeQuizDao\nimport com.infinitepower.newquiz.core.database.dao.SavedMultiChoiceQuestionsDao\nimport com.infinitepower.newquiz.core.database.model.DailyChallengeTaskEntity\nimport com.infinitepower.newquiz.core.database.model.MazeQuizItemEntity\nimport com.infinitepower.newquiz.core.database.model.MultiChoiceQuestionEntity\nimport com.infinitepower.newquiz.core.database.model.user.ComparisonQuizGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.MultiChoiceGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.WordleGameResultEntity\nimport com.infinitepower.newquiz.core.database.util.converters.ListConverter\nimport com.infinitepower.newquiz.core.database.util.converters.LocalDateConverter\nimport com.infinitepower.newquiz.core.database.util.converters.MathFormulaConverter\nimport com.infinitepower.newquiz.core.database.util.converters.QuestionDifficultyConverter\n\n@SuppressLint(\"all\")\n@TypeConverters(\n    ListConverter::class,\n    LocalDateConverter::class,\n    QuestionDifficultyConverter::class,\n    MathFormulaConverter::class\n)\n@Database(\n    entities = [\n        MultiChoiceQuestionEntity::class,\n        MazeQuizItemEntity::class,\n        DailyChallengeTaskEntity::class,\n        MultiChoiceGameResultEntity::class,\n        WordleGameResultEntity::class,\n        ComparisonQuizGameResultEntity::class\n    ],\n    version = 7,\n    exportSchema = true,\n    autoMigrations = [\n        AutoMigration(from = 1, to = 2),\n        AutoMigration(\n            from = 2,\n            to = 3,\n            spec = AppDatabase.RemoveDailyWordleTableMigration::class\n        ),\n        AutoMigration(from = 3, to = 4),\n        AutoMigration(from = 4, to = 5),\n        AutoMigration(\n            from = 5,\n            to = 6,\n            spec = AppDatabase.RemoveComparisonQuizHighestPositionTableMigration::class\n        ),\n        AutoMigration(from = 6, to = 7)\n    ]\n)\nabstract class AppDatabase : RoomDatabase() {\n    abstract fun savedQuestionsDao(): SavedMultiChoiceQuestionsDao\n\n    abstract fun mazeQuizDao(): MazeQuizDao\n\n    abstract fun dailyChallengeDao(): DailyChallengeDao\n\n    abstract fun gameResultDao(): GameResultDao\n\n    companion object {\n        internal const val DATABASE_NAME = \"app-database\"\n    }\n\n    @DeleteTable(tableName = \"wordle_daily_calendar\")\n    class RemoveDailyWordleTableMigration : AutoMigrationSpec\n\n    @DeleteColumn(\n        tableName = \"comparison_quiz_game_results\",\n        columnName = \"highest_position\"\n    )\n    @DeleteTable(tableName = \"comparison_quiz_highest_position\")\n    class RemoveComparisonQuizHighestPositionTableMigration : AutoMigrationSpec\n}\n\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/dao/DailyChallengeDao.kt",
    "content": "package com.infinitepower.newquiz.core.database.dao\n\nimport androidx.room.Dao\nimport androidx.room.Insert\nimport androidx.room.Query\nimport androidx.room.Update\nimport com.infinitepower.newquiz.core.database.model.DailyChallengeTaskEntity\nimport kotlinx.coroutines.flow.Flow\n\n@Dao\ninterface DailyChallengeDao {\n    @Query(\"SELECT * FROM daily_challenge_tasks ORDER BY endDate DESC\")\n    fun getAllTasksFlow(): Flow<List<DailyChallengeTaskEntity>>\n\n    @Query(\"SELECT * FROM daily_challenge_tasks ORDER BY endDate DESC\")\n    suspend fun getAllTasks(): List<DailyChallengeTaskEntity>\n\n    /**\n     * Get all tasks that are available for the current date.\n     * A task is available if the [currentTime] date is between the start date and end date of the task.\n     *\n     * @param currentTime The current date in milliseconds.\n     */\n    @Query(\n        \"\"\"\n        SELECT * FROM daily_challenge_tasks\n        WHERE startDate <= :currentTime AND endDate >= :currentTime\n        ORDER BY endDate DESC\n        \"\"\"\n    )\n    suspend fun getAvailableTasks(currentTime: Long): List<DailyChallengeTaskEntity>\n\n    @Query(\n        \"\"\"\n        SELECT EXISTS(\n            SELECT 1 \n            FROM daily_challenge_tasks \n            WHERE startDate <= :currentTime AND endDate >= :currentTime\n        )\n        \"\"\"\n    )\n    suspend fun tasksAreAvailable(currentTime: Long): Boolean\n\n    @Query(\"SELECT * FROM daily_challenge_tasks WHERE type = :type ORDER BY endDate DESC\")\n    suspend fun getTaskByType(type: String): DailyChallengeTaskEntity?\n\n    @Insert\n    suspend fun insertAll(vararg tasks: DailyChallengeTaskEntity)\n\n    @Insert\n    suspend fun insertAll(tasks: List<DailyChallengeTaskEntity>)\n\n    @Update\n    suspend fun update(vararg tasks: DailyChallengeTaskEntity)\n\n    @Update\n    suspend fun updateAll(tasks: List<DailyChallengeTaskEntity>)\n\n    @Query(\"DELETE FROM daily_challenge_tasks\")\n    suspend fun deleteAll()\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/dao/GameResultDao.kt",
    "content": "package com.infinitepower.newquiz.core.database.dao\n\nimport androidx.annotation.Keep\nimport androidx.room.ColumnInfo\nimport androidx.room.Dao\nimport androidx.room.Insert\nimport androidx.room.OnConflictStrategy\nimport androidx.room.Query\nimport com.infinitepower.newquiz.core.database.model.user.ComparisonQuizGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.MultiChoiceGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.WordleGameResultEntity\nimport kotlinx.coroutines.flow.Flow\n\n@Dao\ninterface GameResultDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertMultiChoiceResult(vararg result: MultiChoiceGameResultEntity)\n\n    @Query(\"SELECT * FROM multi_choice_game_results\")\n    suspend fun getMultiChoiceResults(): List<MultiChoiceGameResultEntity>\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertWordleResult(vararg result: WordleGameResultEntity)\n\n    @Query(\"SELECT * FROM wordle_game_results\")\n    suspend fun getWordleResults(): List<WordleGameResultEntity>\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertComparisonQuizResult(vararg result: ComparisonQuizGameResultEntity)\n\n    @Query(\"SELECT * FROM comparison_quiz_game_results\")\n    suspend fun getComparisonQuizResults(): List<ComparisonQuizGameResultEntity>\n\n    @Query(\"\"\"\n        SELECT end_position\n        FROM comparison_quiz_game_results\n        WHERE category_id = :categoryId\n        ORDER BY end_position DESC\n        LIMIT 1\n    \"\"\")\n    suspend fun getComparisonQuizHighestPosition(\n        categoryId: String\n    ): Int\n\n    @Query(\"\"\"\n        SELECT end_position\n        FROM comparison_quiz_game_results\n        WHERE category_id = :categoryId\n        ORDER BY end_position DESC\n        LIMIT 1\n    \"\"\")\n    fun getComparisonQuizHighestPositionFlow(\n        categoryId: String\n    ): Flow<Int>\n\n    @Keep\n    data class XpForPlayedAt(\n        @ColumnInfo(name = \"earned_xp\")\n        val earnedXp: Int,\n        @ColumnInfo(name = \"played_at\")\n        val playedAt: Long,\n    )\n\n    @Query(\"\"\"\n        SELECT earned_xp, played_at FROM multi_choice_game_results\n        WHERE played_at BETWEEN :startDate AND :endDate\n        UNION ALL\n        SELECT earned_xp, played_at FROM wordle_game_results\n        WHERE played_at BETWEEN :startDate AND :endDate\n        UNION ALL\n        SELECT earned_xp, played_at FROM comparison_quiz_game_results\n        WHERE played_at BETWEEN :startDate AND :endDate\n    \"\"\")\n    suspend fun getXpForDateRange(\n        startDate: Long,\n        endDate: Long\n    ): List<XpForPlayedAt>\n\n    @Query(\"\"\"\n        SELECT earned_xp, played_at FROM multi_choice_game_results\n        WHERE played_at BETWEEN :startDate AND :endDate\n        UNION ALL\n        SELECT earned_xp, played_at FROM wordle_game_results\n        WHERE played_at BETWEEN :startDate AND :endDate\n        UNION ALL\n        SELECT earned_xp, played_at FROM comparison_quiz_game_results\n        WHERE played_at BETWEEN :startDate AND :endDate\n    \"\"\")\n    fun getXpForDateRangeFlow(\n        startDate: Long,\n        endDate: Long\n    ): Flow<List<XpForPlayedAt>>\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/dao/MazeQuizDao.kt",
    "content": "package com.infinitepower.newquiz.core.database.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\nimport androidx.room.OnConflictStrategy\nimport androidx.room.Query\nimport androidx.room.Update\nimport com.infinitepower.newquiz.core.database.model.MazeQuizItemEntity\nimport kotlinx.coroutines.flow.Flow\n\n@Dao\ninterface MazeQuizDao {\n    @Query(\"SELECT * FROM mazeItems\")\n    suspend fun getAllMazeItems(): List<MazeQuizItemEntity>\n\n    @Query(\"SELECT * FROM mazeItems\")\n    fun getAllMazeItemsFlow(): Flow<List<MazeQuizItemEntity>>\n\n    @Query(\"SELECT * FROM mazeItems WHERE id = :id LIMIT 1\")\n    suspend fun getMazeItemById(id: Int): MazeQuizItemEntity?\n\n    @Query(\"SELECT * FROM mazeItems WHERE played = 0 LIMIT 1\")\n    suspend fun getFirstAvailableMazeItem(): MazeQuizItemEntity?\n\n    @Query(\"SELECT COUNT(id) FROM mazeItems\")\n    suspend fun countAllItems(): Int\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertItems(vararg items: MazeQuizItemEntity)\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertItems(items: List<MazeQuizItemEntity>)\n\n    @Update(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun updateItem(item: MazeQuizItemEntity)\n\n    @Delete\n    suspend fun removeItems(items: List<MazeQuizItemEntity>)\n\n    @Query(\"DELETE FROM mazeItems\")\n    suspend fun deleteAll()\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/dao/SavedMultiChoiceQuestionsDao.kt",
    "content": "package com.infinitepower.newquiz.core.database.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\nimport androidx.room.OnConflictStrategy\nimport androidx.room.Query\nimport com.infinitepower.newquiz.core.database.model.MultiChoiceQuestionEntity\nimport kotlinx.coroutines.flow.Flow\n\n@Dao\ninterface SavedMultiChoiceQuestionsDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertQuestions(questions: List<MultiChoiceQuestionEntity>)\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertQuestions(vararg questions: MultiChoiceQuestionEntity)\n\n    @Query(\"SELECT * FROM saved_multi_choice_questions\")\n    fun getFlowQuestions(): Flow<List<MultiChoiceQuestionEntity>>\n\n    @Query(\"SELECT * FROM saved_multi_choice_questions ORDER BY description ASC\")\n    fun getFlowQuestionsSortedByDescription(): Flow<List<MultiChoiceQuestionEntity>>\n\n    @Query(\"SELECT * FROM saved_multi_choice_questions ORDER BY category ASC\")\n    fun getFlowQuestionsSortedByCategory(): Flow<List<MultiChoiceQuestionEntity>>\n\n    @Query(\"SELECT * FROM saved_multi_choice_questions\")\n    suspend fun getQuestions(): List<MultiChoiceQuestionEntity>\n\n    @Query(\"SELECT count(*) FROM saved_multi_choice_questions\")\n    fun getCount(): Flow<Int>\n\n    @Delete\n    suspend fun deleteAll(questions: List<MultiChoiceQuestionEntity>)\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/di/DaoModule.kt",
    "content": "package com.infinitepower.newquiz.core.database.di\n\nimport com.infinitepower.newquiz.core.database.AppDatabase\nimport com.infinitepower.newquiz.core.database.dao.DailyChallengeDao\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.database.dao.MazeQuizDao\nimport com.infinitepower.newquiz.core.database.dao.SavedMultiChoiceQuestionsDao\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject DaoModule {\n    @Provides\n    fun provideSavedQuestionsDao(\n        appDatabase: AppDatabase\n    ): SavedMultiChoiceQuestionsDao = appDatabase.savedQuestionsDao()\n\n    @Provides\n    fun provideMazeQuizDao(\n        appDatabase: AppDatabase\n    ): MazeQuizDao = appDatabase.mazeQuizDao()\n\n    @Provides\n    fun provideDailyChallengeDao(\n        appDatabase: AppDatabase\n    ): DailyChallengeDao = appDatabase.dailyChallengeDao()\n\n    @Provides\n    fun provideGameResultDao(\n        appDatabase: AppDatabase\n    ): GameResultDao = appDatabase.gameResultDao()\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/di/DatabaseModule.kt",
    "content": "package com.infinitepower.newquiz.core.database.di\n\nimport android.content.Context\nimport androidx.room.Room\nimport com.infinitepower.newquiz.core.database.AppDatabase\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject DatabaseModule {\n    @Provides\n    @Singleton\n    fun provideAppDatabase(\n        @ApplicationContext applicationContext: Context\n    ): AppDatabase = Room.databaseBuilder(\n        applicationContext,\n        AppDatabase::class.java,\n        AppDatabase.DATABASE_NAME\n    ).build()\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/model/DailyChallengeTaskEntity.kt",
    "content": "package com.infinitepower.newquiz.core.database.model\n\nimport androidx.annotation.Keep\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Keep\n@Entity(tableName = \"daily_challenge_tasks\")\ndata class DailyChallengeTaskEntity(\n    @PrimaryKey(autoGenerate = true) val id: Int = 0,\n    val diamondsReward: Int,\n    val experienceReward: Int,\n    val isClaimed: Boolean,\n    val currentValue: Int,\n    val maxValue: Int,\n    val type: String,\n    val startDate: Long,\n    val endDate: Long\n)\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/model/MazeQuizItemEntity.kt",
    "content": "package com.infinitepower.newquiz.core.database.model\n\nimport androidx.annotation.Keep\nimport androidx.room.Embedded\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\n\n@Keep\n@Entity(tableName = \"mazeItems\")\ndata class MazeQuizItemEntity(\n    // Shared columns\n    @PrimaryKey(autoGenerate = true) val id: Int = 0,\n    val difficulty: QuestionDifficulty,\n    val played: Boolean,\n    val type: GameMode,\n    val mazeSeed: Int,\n\n    // Multi choice quiz\n    @Embedded(\"maze_question_\")\n    val multiChoiceQuestion: MultiChoiceQuestionEntity? = null,\n\n    // Wordle\n    @Embedded(\"maze_wordle_\")\n    val wordleItem: WordleEntity? = null,\n\n    // Comparison quiz\n    @Embedded(\"comparison_quiz_\")\n    val comparisonQuizQuestion: ComparisonQuizEntity? = null\n) {\n    @Keep\n    data class WordleEntity(\n        val wordleWord: String,\n        val wordleQuizType: WordleQuizType,\n        val textHelper: String? = null\n    )\n\n    @Keep\n    data class ComparisonQuizEntity(\n        val category: String,\n        val comparisonMode: ComparisonMode,\n        @Embedded(\"first_question_\")\n        val firstQuestion: ComparisonQuizItemEntity,\n        @Embedded(\"second_question_\")\n        val secondQuestion: ComparisonQuizItemEntity\n    )\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/model/MultiChoiceQuestionEntity.kt",
    "content": "package com.infinitepower.newquiz.core.database.model\n\nimport androidx.annotation.Keep\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\nimport kotlinx.serialization.Serializable\n\n/**\n * Multi choice question entity, used to save items in database.\n */\n@Keep\n@Serializable\n@Entity(tableName = \"saved_multi_choice_questions\")\ndata class MultiChoiceQuestionEntity(\n    @PrimaryKey(autoGenerate = true) val id: Int = 0,\n    val description: String,\n    @ColumnInfo(name = \"image_url\") val imageUrl: String? = null,\n    val answers: List<String>,\n    val lang: String,\n    val category: String,\n    @ColumnInfo(name = \"correct_ans\") val correctAns: Int,\n    val type: String,\n    val difficulty: String\n) : java.io.Serializable {\n    override fun equals(other: Any?): Boolean {\n        if (other !is MultiChoiceQuestionEntity) return false\n\n        val idEquals = id == other.id\n        val descriptionEquals = description == other.description\n        val imageUrlEquals = imageUrl == other.imageUrl\n        val answersEquals = answers == other.answers\n        val langEquals = lang == other.lang\n        val categoryEquals = category == other.category\n        val correctAnsEquals = correctAns == other.correctAns\n        val typeEquals = type == other.type\n        val difficultyEquals = difficulty == other.difficulty\n\n        return idEquals\n                && descriptionEquals\n                && imageUrlEquals\n                && answersEquals\n                && langEquals\n                && categoryEquals\n                && correctAnsEquals\n                && typeEquals\n                && difficultyEquals\n    }\n\n    override fun hashCode(): Int {\n        var result = id\n        result = 31 * result + description.hashCode()\n        result = 31 * result + (imageUrl?.hashCode() ?: 0)\n        result = 31 * result + answers.hashCode()\n        result = 31 * result + lang.hashCode()\n        result = 31 * result + category.hashCode()\n        result = 31 * result + correctAns\n        result = 31 * result + type.hashCode()\n        result = 31 * result + difficulty.hashCode()\n        return result\n    }\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/model/user/BaseGameResultEntity.kt",
    "content": "package com.infinitepower.newquiz.core.database.model.user\n\ninterface BaseGameResultEntity {\n    val gameId: Int\n    val earnedXp: Int\n    val playedAt: Long\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/model/user/ComparisonQuizGameResultEntity.kt",
    "content": "package com.infinitepower.newquiz.core.database.model.user\n\nimport androidx.annotation.Keep\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Keep\n@Entity(tableName = \"comparison_quiz_game_results\")\ndata class ComparisonQuizGameResultEntity(\n    @PrimaryKey(autoGenerate = true)\n    @ColumnInfo(name = \"game_id\")\n    override val gameId: Int = 0,\n    @ColumnInfo(name = \"earned_xp\")\n    override val earnedXp: Int,\n    @ColumnInfo(name = \"played_at\")\n    override val playedAt: Long,\n    @ColumnInfo(name = \"category_id\")\n    val categoryId: String,\n    @ColumnInfo(name = \"comparison_mode\")\n    val comparisonMode: String,\n    @ColumnInfo(name = \"end_position\")\n    val endPosition: Int,\n    @ColumnInfo(name = \"skipped_answers\")\n    val skippedAnswers: Int\n) : BaseGameResultEntity\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/model/user/MultiChoiceGameResultEntity.kt",
    "content": "package com.infinitepower.newquiz.core.database.model.user\n\nimport androidx.annotation.Keep\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Keep\n@Entity(\n    tableName = \"multi_choice_game_results\"\n)\ndata class MultiChoiceGameResultEntity(\n    @PrimaryKey(autoGenerate = true)\n    @ColumnInfo(name = \"game_id\")\n    override val gameId: Int = 0,\n    @ColumnInfo(name = \"correct_answers\")\n    val correctAnswers: Int,\n    @ColumnInfo(name = \"skipped_questions\")\n    val skippedQuestions: Int,\n    @ColumnInfo(name = \"question_count\")\n    val questionCount: Int,\n    @ColumnInfo(name = \"average_answer_time\")\n    val averageAnswerTime: Double,\n    @ColumnInfo(name = \"earned_xp\")\n    override val earnedXp: Int,\n    @ColumnInfo(name = \"played_at\")\n    override val playedAt: Long,\n) : BaseGameResultEntity\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/model/user/WordleGameResultEntity.kt",
    "content": "package com.infinitepower.newquiz.core.database.model.user\n\nimport androidx.annotation.Keep\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Keep\n@Entity(\n    tableName = \"wordle_game_results\"\n)\ndata class WordleGameResultEntity(\n    @PrimaryKey(autoGenerate = true)\n    @ColumnInfo(name = \"game_id\")\n    override val gameId: Int = 0,\n    @ColumnInfo(name = \"earned_xp\")\n    override val earnedXp: Int,\n    @ColumnInfo(name = \"played_at\")\n    override val playedAt: Long,\n    @ColumnInfo(name = \"word_length\")\n    val wordLength: Int,\n    @ColumnInfo(name = \"rows_used\")\n    val rowsUsed: Int,\n    @ColumnInfo(name = \"max_rows\")\n    val maxRows: Int,\n    @ColumnInfo(name = \"category_id\")\n    val categoryId: String,\n) : BaseGameResultEntity\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/util/converters/ListConverter.kt",
    "content": "package com.infinitepower.newquiz.core.database.util.converters\n\nimport androidx.room.TypeConverter\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\n\nclass ListConverter {\n    @TypeConverter\n    fun stringListToJson(value: List<String>): String = Json.encodeToString(value)\n\n    @TypeConverter\n    fun jsonToStringList(value: String): List<String> = Json.decodeFromString(value)\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/util/converters/LocalDateConverter.kt",
    "content": "package com.infinitepower.newquiz.core.database.util.converters\n\nimport androidx.room.TypeConverter\nimport kotlinx.datetime.LocalDate\n\nclass LocalDateConverter {\n    @TypeConverter\n    fun localDateToJson(value: LocalDate): String = value.toString()\n\n    @TypeConverter\n    fun stringToLocalDate(value: String): LocalDate = LocalDate.parse(value)\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/util/converters/MathFormulaConverter.kt",
    "content": "package com.infinitepower.newquiz.core.database.util.converters\n\nimport androidx.room.TypeConverter\nimport com.infinitepower.newquiz.model.math_quiz.MathFormula\n\nclass MathFormulaConverter {\n    @TypeConverter\n    fun mathFormulaToJson(value: MathFormula): String = value.fullFormula\n\n    @TypeConverter\n    fun stringToMathFormula(value: String): MathFormula = MathFormula.fromStringFullFormula(value)\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/util/converters/QuestionDifficultyConverter.kt",
    "content": "package com.infinitepower.newquiz.core.database.util.converters\n\nimport androidx.room.TypeConverter\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\n\nclass QuestionDifficultyConverter {\n    @TypeConverter\n    fun localDateToJson(value: QuestionDifficulty): String = value.id\n\n    @TypeConverter\n    fun stringToLocalDate(value: String): QuestionDifficulty = QuestionDifficulty.from(value)\n}\n"
  },
  {
    "path": "core/database/src/main/kotlin/com/infinitepower/newquiz/core/database/util/mappers/MultiChoiceQuestionMapper.kt",
    "content": "package com.infinitepower.newquiz.core.database.util.mappers\n\nimport com.infinitepower.newquiz.core.database.model.MultiChoiceQuestionEntity\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport java.net.URI\n\nfun MultiChoiceQuestion.toEntity(): MultiChoiceQuestionEntity {\n    return MultiChoiceQuestionEntity(\n        id = id,\n        description = description,\n        imageUrl = image?.toASCIIString(),\n        answers = answers,\n        lang = lang.name,\n        category = category.toString(),\n        correctAns = correctAns,\n        type = type.name,\n        difficulty = difficulty.toString()\n    )\n}\n\nfun MultiChoiceQuestionEntity.toModel(): MultiChoiceQuestion = MultiChoiceQuestion(\n    id = id,\n    description = description,\n    image = imageUrl?.let { URI.create(it) },\n    answers = answers,\n    lang = QuestionLanguage.EN,\n    category = MultiChoiceBaseCategory.fromId(category),\n    correctAns = correctAns,\n    type = MultiChoiceQuestionType.MULTIPLE,\n    difficulty = QuestionDifficulty.from(difficulty)\n)\n"
  },
  {
    "path": "core/datastore/.gitignore",
    "content": "/build"
  },
  {
    "path": "core/datastore/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.core.datastore\"\n}\n\ndependencies {\n    api(libs.androidx.dataStore.preferences)\n\n    implementation(projects.model)\n    implementation(projects.core)\n}\n"
  },
  {
    "path": "core/datastore/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "core/datastore/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "core/datastore/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/PreferenceRequest.kt",
    "content": "package com.infinitepower.newquiz.core.datastore\n\nimport androidx.datastore.preferences.core.Preferences\n\nopen class PreferenceRequest<T>(\n    val key: Preferences.Key<T>,\n    val defaultValue: T\n)\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/common/LocalUserCommon.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.common\n\nimport android.content.Context\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.intPreferencesKey\nimport androidx.datastore.preferences.core.longPreferencesKey\nimport androidx.datastore.preferences.core.stringPreferencesKey\nimport androidx.datastore.preferences.preferencesDataStore\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport java.util.UUID\n\nval Context.localUserDataStore: DataStore<Preferences> by preferencesDataStore(name = \"local_user\")\n\nobject LocalUserCommon {\n    object UserUid : PreferenceRequest<String>(\n        key = stringPreferencesKey(\"uid\"),\n        defaultValue = UUID.randomUUID().toString()\n    )\n\n    object UserTotalXp : PreferenceRequest<Long>(\n        key = longPreferencesKey(\"totalXp\"),\n        defaultValue = 0\n    )\n\n    data class UserDiamonds(\n        val initialDiamonds: Int = 0,\n    ) : PreferenceRequest<Int>(\n        key = intPreferencesKey(\"diamonds\"),\n        defaultValue = initialDiamonds\n    )\n}\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/common/RecentCategoryDataStoreCommon.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.common\n\nimport android.content.Context\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.stringSetPreferencesKey\nimport androidx.datastore.preferences.preferencesDataStore\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\n\nval Context.recentCategoriesDataStore: DataStore<Preferences> by preferencesDataStore(name = \"recent_categories\")\n\nobject RecentCategoryDataStoreCommon {\n    object MultiChoice : PreferenceRequest<Set<String>>(stringSetPreferencesKey(\"multi_choice\"), emptySet())\n\n    object Wordle : PreferenceRequest<Set<String>>(stringSetPreferencesKey(\"wordle\"), emptySet())\n\n    object ComparisonQuiz : PreferenceRequest<Set<String>>(stringSetPreferencesKey(\"comparison_quiz\"), emptySet())\n}\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/common/SettingsCommon.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.common\n\nimport android.content.Context\nimport androidx.annotation.Keep\nimport androidx.annotation.RawRes\nimport androidx.annotation.StringRes\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.booleanPreferencesKey\nimport androidx.datastore.preferences.core.intPreferencesKey\nimport androidx.datastore.preferences.core.stringPreferencesKey\nimport androidx.datastore.preferences.preferencesDataStore\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.core.R as CoreR\nimport java.util.Locale\n\nval Context.settingsDataStore: DataStore<Preferences> by preferencesDataStore(name = \"settings\")\n\nobject SettingsCommon {\n    object MultiChoiceQuizQuestionsSize : PreferenceRequest<Int>(\n        key = intPreferencesKey(\"quickQuizQuestionsSize\"),\n        defaultValue = 5\n    )\n\n    object HideOnlineCategories : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"hideOnlineCategories\"),\n        defaultValue = false\n    )\n\n    @Keep\n    data class CategoryConnectionInfoBadge(\n        val default: ShowCategoryConnectionInfo = ShowCategoryConnectionInfo.NONE\n    ) : PreferenceRequest<String>(\n        key = stringPreferencesKey(\"categoryConnectionInfoBadge\"),\n        defaultValue = default.name\n    )\n\n    object InfiniteWordleQuizLanguage : PreferenceRequest<String>(\n        key = stringPreferencesKey(\"infiniteWordleQuizLanguage\"),\n        defaultValue = getInfiniteWordleDefaultLang()\n    )\n\n    object WordleInfiniteRowsLimited : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"wordleInfiniteRowsLimited\"),\n        defaultValue = false\n    )\n\n    object WordleInfiniteRowsLimit : PreferenceRequest<Int>(\n        key = intPreferencesKey(\"wordleInfiniteRowsLimit\"),\n        defaultValue = 6\n    )\n\n    object WordleHardMode : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"wordleHardMode\"),\n        defaultValue = false\n    )\n\n    object WordleColorBlindMode : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"wordleColorBlindMode\"),\n        defaultValue = false\n    )\n\n    object WordleLetterHints : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"wordleLetterHints\"),\n        defaultValue = false\n    )\n\n    object GlobalAnimationsEnabled : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"animations_enabled\"),\n        defaultValue = true\n    )\n\n    object WordleAnimationsEnabled : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"wordle_animations_enabled\"),\n        defaultValue = true\n    )\n\n    object MultiChoiceAnimationsEnabled : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"multi_choice_animations_enabled\"),\n        defaultValue = true\n    )\n\n    object MazeAutoScrollToCurrentItem : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"mazeAutoScrollToCurrentItem\"),\n        defaultValue = true\n    )\n\n    object TemperatureUnit : PreferenceRequest<String>(\n        key = stringPreferencesKey(\"temperatureUnit\"),\n        defaultValue = \"\"\n    )\n\n    object DistanceUnitType : PreferenceRequest<String>(\n        key = stringPreferencesKey(\"distanceUnitType\"),\n        defaultValue = \"\"\n    )\n}\n\n@Keep\ndata class SettingsWordleLang(\n    val key: String,\n    @StringRes val languageId: Int,\n    @RawRes val rawListId: Int\n)\n\nval textWordleSupportedLang = listOf(\n    SettingsWordleLang(\n        key = \"en\",\n        languageId = CoreR.string.english,\n        rawListId = CoreR.raw.wordle_list\n    ),\n    SettingsWordleLang(\n        key = \"pt\",\n        languageId = CoreR.string.portuguese,\n        rawListId = CoreR.raw.wordle_list_pt\n    ),\n    SettingsWordleLang(\n        key = \"es\",\n        languageId = CoreR.string.spanish,\n        rawListId = CoreR.raw.wordle_list_es\n    ),\n    SettingsWordleLang(\n        key = \"fr\",\n        languageId = CoreR.string.french,\n        rawListId = CoreR.raw.wordle_list_fr\n    )\n)\n\nprivate fun getInfiniteWordleDefaultLang(): String {\n    val localeLanguage = Locale.getDefault().language\n    val langKeys = textWordleSupportedLang.map { it.key }\n    return if (localeLanguage in langKeys) localeLanguage else \"en\"\n}\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/di/LocalUserDatastoreModule.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.di\n\nimport android.content.Context\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport com.infinitepower.newquiz.core.datastore.common.localUserDataStore\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.PreferencesDatastoreManager\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Qualifier\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject LocalUserDataStoreModule {\n    @Provides\n    @Singleton\n    @LocalUserDataStore\n    fun provideLocalUserDatastore(\n        @ApplicationContext context: Context\n    ): DataStore<Preferences> = context.localUserDataStore\n\n    @Provides\n    @Singleton\n    @LocalUserDataStoreManager\n    fun provideLocalUserDataStoreManager(\n        @LocalUserDataStore dataStore: DataStore<Preferences>\n    ): DataStoreManager = PreferencesDatastoreManager(dataStore)\n}\n\n@Qualifier\n@Retention(AnnotationRetention.RUNTIME)\nannotation class LocalUserDataStore\n\n@Qualifier\n@Retention(AnnotationRetention.RUNTIME)\nannotation class LocalUserDataStoreManager\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/di/RecentCategoriesDatastoreModule.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.di\n\nimport android.content.Context\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport com.infinitepower.newquiz.core.datastore.common.recentCategoriesDataStore\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.PreferencesDatastoreManager\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Qualifier\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject RecentCategoriesDatastoreModule {\n    @Provides\n    @Singleton\n    @RecentCategoriesDataStore\n    fun provideRecentCategoriesDatastore(\n        @ApplicationContext context: Context\n    ): DataStore<Preferences> = context.recentCategoriesDataStore\n\n    @Provides\n    @Singleton\n    @RecentCategoriesDataStoreManager\n    fun provideRecentCategoriesStoreManager(\n        @RecentCategoriesDataStore dataStore: DataStore<Preferences>\n    ): DataStoreManager = PreferencesDatastoreManager(dataStore)\n}\n\n@Qualifier\n@Retention(AnnotationRetention.RUNTIME)\nannotation class RecentCategoriesDataStore\n\n@Qualifier\n@Retention(AnnotationRetention.RUNTIME)\nannotation class RecentCategoriesDataStoreManager\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/di/SettingsDataStoreModule.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.di\n\nimport android.content.Context\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport com.infinitepower.newquiz.core.datastore.common.settingsDataStore\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.PreferencesDatastoreManager\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Qualifier\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject SettingsDataStoreModule {\n    @Provides\n    @Singleton\n    @SettingsDataStore\n    fun provideSettingsDatastore(\n        @ApplicationContext context: Context\n    ): DataStore<Preferences> = context.settingsDataStore\n\n    @Provides\n    @Singleton\n    @SettingsDataStoreManager\n    fun provideDataStoreManager(\n        @SettingsDataStore dataStore: DataStore<Preferences>\n    ): DataStoreManager = PreferencesDatastoreManager(dataStore)\n}\n\n@Qualifier\n@Retention(AnnotationRetention.RUNTIME)\nannotation class SettingsDataStore\n\n@Qualifier\n@Retention(AnnotationRetention.RUNTIME)\nannotation class SettingsDataStoreManager\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/manager/DataStoreManager.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.manager\n\nimport androidx.datastore.preferences.core.Preferences\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport kotlinx.coroutines.flow.Flow\n\ninterface DataStoreManager {\n    val preferenceFlow: Flow<Preferences>\n\n    suspend fun <T> getPreference(preferenceEntry: PreferenceRequest<T>): T\n\n    fun <T> getPreferenceFlow(request: PreferenceRequest<T>): Flow<T>\n\n    suspend fun <T> editPreference(key: Preferences.Key<T>, newValue: T)\n\n    suspend fun editPreferences(vararg prefs: Preferences.Pair<*>)\n\n    suspend fun clearPreferences()\n}\n"
  },
  {
    "path": "core/datastore/src/main/kotlin/com/infinitepower/newquiz/core/datastore/manager/PreferencesDatastoreManager.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.manager\n\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.edit\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport kotlinx.coroutines.flow.distinctUntilChanged\nimport kotlinx.coroutines.flow.firstOrNull\nimport kotlinx.coroutines.flow.map\n\nclass PreferencesDatastoreManager(\n    private val dataStore: DataStore<Preferences>\n) : DataStoreManager {\n    override val preferenceFlow = dataStore.data\n\n    override suspend fun <T> getPreference(\n        preferenceEntry: PreferenceRequest<T>\n    ) = preferenceFlow\n        .firstOrNull()\n        ?.get(preferenceEntry.key) ?: preferenceEntry.defaultValue\n\n    override fun <T> getPreferenceFlow(request: PreferenceRequest<T>) = preferenceFlow.map {\n        it[request.key] ?: request.defaultValue\n    }.distinctUntilChanged()\n\n    override suspend fun <T> editPreference(key: Preferences.Key<T>, newValue: T) {\n        dataStore.edit { preferences -> preferences[key] = newValue }\n    }\n\n    override suspend fun editPreferences(vararg prefs: Preferences.Pair<*>) {\n        dataStore.edit { preferences ->\n            preferences.putAll(*prefs)\n        }\n    }\n\n    override suspend fun clearPreferences() {\n        dataStore.edit { preferences -> preferences.clear() }\n    }\n}\n"
  },
  {
    "path": "core/datastore/src/normal/kotlin/com/infinitepower/newquiz/core/datastore/common/DataAnalyticsCommon.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.common\n\nimport android.content.Context\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.booleanPreferencesKey\nimport androidx.datastore.preferences.core.stringPreferencesKey\nimport androidx.datastore.preferences.preferencesDataStore\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport com.infinitepower.newquiz.model.DataAnalyticsConsentState\n\nval Context.dataAnalyticsDataStore: DataStore<Preferences> by preferencesDataStore(name = \"data_analytics\")\n\nobject DataAnalyticsCommon {\n    object DataAnalyticsConsent : PreferenceRequest<String>(stringPreferencesKey(\"dataAnalyticsConsent\"), DataAnalyticsConsentState.NONE.name)\n\n    object GloballyAnalyticsCollectionEnabled : PreferenceRequest<Boolean>(booleanPreferencesKey(\"dataAnalyticsEnabled\"), false)\n\n    object GeneralAnalyticsEnabled : PreferenceRequest<Boolean>(booleanPreferencesKey(\"generalAnalyticsEnabled\"), false)\n\n    object CrashlyticsEnabled : PreferenceRequest<Boolean>(booleanPreferencesKey(\"crashlyticsEnabled\"), false)\n\n    object PerformanceMonitoringEnabled : PreferenceRequest<Boolean>(booleanPreferencesKey(\"performanceMonitoringEnabled\"), false)\n}"
  },
  {
    "path": "core/datastore/src/normal/kotlin/com/infinitepower/newquiz/core/datastore/common/TranslationCommon.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.common\n\nimport androidx.datastore.preferences.core.booleanPreferencesKey\nimport androidx.datastore.preferences.core.stringPreferencesKey\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\n\nobject TranslationCommon {\n    /**\n     * Returns whether the translation is enabled.\n     */\n    object Enabled : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"translationEnabled\"),\n        defaultValue = false\n    )\n\n    /**\n     * Returns the target language for the app translation.\n     * When empty, there is no translation target language.\n     */\n    object TargetLanguage : PreferenceRequest<String>(\n        key = stringPreferencesKey(\"translationTargetLanguage\"),\n        defaultValue = \"\"\n    )\n\n    /**\n     * Preference to store whether the user wants to download the translation model over wifi only.\n     */\n    object RequireWifi : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"translationRequireWifi\"),\n        defaultValue = true\n    )\n\n    /**\n     * Preference to store whether the user wants to download the translation model only when charging.\n     */\n    object RequireCharging : PreferenceRequest<Boolean>(\n        key = booleanPreferencesKey(\"translationRequireCharging\"),\n        defaultValue = false\n    )\n}"
  },
  {
    "path": "core/datastore/src/normal/kotlin/com/infinitepower/newquiz/core/datastore/di/DataAnalyticsDatastoreModule.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.di\n\nimport android.content.Context\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport com.infinitepower.newquiz.core.datastore.common.dataAnalyticsDataStore\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.PreferencesDatastoreManager\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Qualifier\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject DataAnalyticsDatastoreModule {\n    @Provides\n    @Singleton\n    @DataAnalyticsDataStore\n    fun provideDataAnalyticsDatastore(\n        @ApplicationContext context: Context\n    ): DataStore<Preferences> = context.dataAnalyticsDataStore\n\n    @Provides\n    @Singleton\n    @DataAnalyticsDataStoreManager\n    fun provideDataAnalyticsDataStoreManager(\n        @DataAnalyticsDataStore dataStore: DataStore<Preferences>\n    ): DataStoreManager = PreferencesDatastoreManager(dataStore)\n}\n\n@Qualifier\n@Retention(AnnotationRetention.RUNTIME)\nannotation class DataAnalyticsDataStore\n\n@Qualifier\n@Retention(AnnotationRetention.RUNTIME)\nannotation class DataAnalyticsDataStoreManager\n"
  },
  {
    "path": "core/datastore/src/test/kotlin/com/infinitepower/newquiz/core/datastore/manager/PreferencesDatastoreManagerTest.kt",
    "content": "package com.infinitepower.newquiz.core.datastore.manager\n\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.PreferenceDataStoreFactory\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.stringPreferencesKey\nimport app.cash.turbine.test\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.test.TestScope\nimport kotlinx.coroutines.test.UnconfinedTestDispatcher\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.api.io.TempDir\nimport java.io.File\nimport kotlin.test.AfterTest\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n@OptIn(ExperimentalCoroutinesApi::class)\ninternal class PreferencesDatastoreManagerTest {\n    private lateinit var dataStoreManager: DataStoreManager\n\n    private val testDispatcher = UnconfinedTestDispatcher()\n    private val testScope = TestScope(testDispatcher + Job())\n\n    @field:TempDir\n    lateinit var tempFile: File\n\n    private val testDataStore: DataStore<Preferences> = PreferenceDataStoreFactory.create(\n        scope = testScope,\n        produceFile = { tempFile.resolve(\"test.preferences_pb\") }\n    )\n\n    @BeforeTest\n    fun setup() {\n        dataStoreManager = PreferencesDatastoreManager(testDataStore)\n    }\n\n    @AfterTest\n    fun tearDown() {\n        tempFile.delete()\n    }\n\n    @Test\n    fun `getPreference returns default value`() = runTest {\n        val key = stringPreferencesKey(\"key\")\n        val defaultValue = \"default\"\n\n        val request = PreferenceRequest(key, defaultValue)\n        val result = dataStoreManager.getPreference(request)\n\n        assertThat(result).isEqualTo(defaultValue)\n    }\n\n    @Test\n    fun `getPreference returns new data when edited`() = runTest {\n        val key = stringPreferencesKey(\"key\")\n        val defaultValue = \"default\"\n\n        val request = PreferenceRequest(key, defaultValue)\n\n        val newValue = \"test\"\n        dataStoreManager.editPreference(key, newValue)\n\n        val result = dataStoreManager.getPreference(request)\n\n        assertThat(result).isEqualTo(newValue)\n    }\n\n    @Test\n    fun `getPreferenceFlow returns correct value`() = runTest {\n        val key = stringPreferencesKey(\"key\")\n        val defaultValue = \"default\"\n\n        val request = PreferenceRequest(key, defaultValue)\n\n\n        dataStoreManager.getPreferenceFlow(request).test {\n            assertThat(awaitItem()).isEqualTo(defaultValue)\n\n            dataStoreManager.editPreference(key, \"test\")\n            assertThat(awaitItem()).isEqualTo(\"test\")\n        }\n    }\n\n    @Test\n    fun `test edit and clear multiple preferences`() = runTest {\n        val request1 = PreferenceRequest(\n            key = stringPreferencesKey(\"key1\"),\n            defaultValue = \"default1\"\n        )\n        val request2 = PreferenceRequest(\n            key = stringPreferencesKey(\"key2\"),\n            defaultValue = \"default2\"\n        )\n\n        dataStoreManager.editPreferences(\n            request1.key to \"a\",\n            request2.key to \"b\"\n        )\n\n        // Check that the preferences were set\n        assertThat(dataStoreManager.getPreference(request1)).isEqualTo(\"a\")\n        assertThat(dataStoreManager.getPreference(request2)).isEqualTo(\"b\")\n\n        dataStoreManager.clearPreferences()\n\n        // Check that the preferences were cleared\n        assertThat(dataStoreManager.getPreference(request1)).isEqualTo(request1.defaultValue)\n        assertThat(dataStoreManager.getPreference(request2)).isEqualTo(request2.defaultValue)\n    }\n}\n"
  },
  {
    "path": "core/proguard-rules.pro",
    "content": "-dontwarn java.lang.invoke.StringConcatFactory"
  },
  {
    "path": "core/remote-config/.gitignore",
    "content": "/build"
  },
  {
    "path": "core/remote-config/README.md",
    "content": "# Remote Config Module (:core:remote_config)\n"
  },
  {
    "path": "core/remote-config/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.core.remote_config\"\n}\n\ndependencies {\n    implementation(libs.androidx.startup.runtime)\n\n    normalImplementation(platform(libs.firebase.bom))\n    normalImplementation(libs.firebase.remoteConfig)\n\n    implementation(projects.model)\n    normalImplementation(projects.core)\n}\n"
  },
  {
    "path": "core/remote-config/src/foss/kotlin/com/infinitepower/newquiz/core/remote_config/initializer/RemoteConfigInitializer.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config.initializer\n\nimport android.content.Context\nimport android.util.Log\nimport androidx.startup.Initializer\nimport com.infinitepower.newquiz.core.remote_config.LocalDefaultsRemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nclass RemoteConfigInitializer : Initializer<RemoteConfig> {\n    private companion object {\n        private const val TAG = \"RemoteConfigInitializer\"\n    }\n\n    @Provides\n    @Singleton\n    override fun create(@ApplicationContext context: Context): RemoteConfig {\n        Log.d(TAG, \"Initializing local remote config\")\n\n        val remoteConfig = LocalDefaultsRemoteConfig(context = context)\n        remoteConfig.initialize()\n\n        Log.d(TAG, \"Local remote config initialized successfully\")\n\n        return remoteConfig\n    }\n\n    override fun dependencies(): List<Class<out Initializer<*>>> {\n        return emptyList()\n    }\n}\n"
  },
  {
    "path": "core/remote-config/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n    <application>\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            android:exported=\"false\"\n            tools:node=\"merge\">\n            <meta-data\n                android:name=\"com.infinitepower.newquiz.core.remote_config.initializer.RemoteConfigInitializer\"\n                android:value=\"androidx.startup\" />\n        </provider>\n    </application>\n</manifest>"
  },
  {
    "path": "core/remote-config/src/main/kotlin/com/infinitepower/newquiz/core/remote_config/LocalDefaultsRemoteConfig.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config\n\nimport android.content.Context\n\nclass NoValueForRemoteConfigKeyException(key: String) : IllegalArgumentException(\"No value for key $key\")\n\nclass LocalDefaultsRemoteConfig (\n    private val context: Context\n) : RemoteConfig {\n    private val remoteConfigValues = mutableMapOf<String, String>()\n\n    override fun initialize(fetchInterval: Long) {\n        val parsedXmlValues = RemoteConfigXmlParser.parse(\n            context = context,\n            xmlResId = R.xml.remote_config_defaults\n        )\n\n        remoteConfigValues.clear()\n        remoteConfigValues.putAll(parsedXmlValues)\n    }\n\n    override fun getString(key: String): String {\n        return remoteConfigValues[key] ?: throw NoValueForRemoteConfigKeyException(key)\n    }\n\n    override fun getLong(key: String): Long {\n        return remoteConfigValues[key]?.toLong() ?: throw NoValueForRemoteConfigKeyException(key)\n    }\n\n    override fun getBoolean(key: String): Boolean {\n        return remoteConfigValues[key]?.toBoolean() ?: throw NoValueForRemoteConfigKeyException(key)\n    }\n}\n"
  },
  {
    "path": "core/remote-config/src/main/kotlin/com/infinitepower/newquiz/core/remote_config/RemoteConfig.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config\n\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.json.Json\n\ninterface RemoteConfig {\n    fun initialize(\n        fetchInterval: Long = 3600L,\n    )\n\n    fun getString(key: String): String\n\n    fun getLong(key: String): Long\n\n    fun getInt(key: String): Int = getLong(key).toInt()\n\n    fun getBoolean(key: String): Boolean\n}\n\n/**\n * Gets the value for the given [removeConfigValue] from the remote config.\n * The type of the value is inferred from the type of the [removeConfigValue].\n *\n * Supported types:\n * - [String]\n * - [Long]\n * - [Int]\n * - [Boolean]\n * - [Enum]\n * - Custom Class (must be annotated with [Serializable] annotation)\n *\n * @param removeConfigValue the value to get from the remote config.\n * @return the value for the given [removeConfigValue].\n * @throws IllegalArgumentException if the type of the [removeConfigValue] is not supported.\n */\ninline fun <reified T> RemoteConfig.get(removeConfigValue: RemoteConfigValue<T>): T {\n    return when (T::class) {\n        String::class -> getString(removeConfigValue.key) as T\n        Long::class -> getLong(removeConfigValue.key) as T\n        Int::class -> getInt(removeConfigValue.key) as T\n        Boolean::class -> getBoolean(removeConfigValue.key) as T\n        else -> {\n            // Check if the type is a custom class with serialization, if so, decode the serialized value.\n            // If the type is an enum with serialization, decode the enum value using the deserialization.\n            if (T::class.java.isAnnotationPresent(Serializable::class.java)) {\n                val serializedValue = getString(removeConfigValue.key)\n\n                return Json.decodeFromString(serializedValue)\n            } else if (T::class.java.isEnum) {\n                // If the type is an enum without serialization, decode the enum value using reflection.\n                val enumValue = getString(removeConfigValue.key)\n\n                return T::class.java.enumConstants\n                    ?.find { it.toString() == enumValue }\n                    ?: throw IllegalArgumentException(\"Invalid enum value: $enumValue\")\n            }\n\n            throw IllegalArgumentException(\"Unsupported type ${T::class}\")\n        }\n    }\n}\n"
  },
  {
    "path": "core/remote-config/src/main/kotlin/com/infinitepower/newquiz/core/remote_config/RemoteConfigValue.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config\n\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizHelperValueState\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\n\n@JvmInline\nvalue class RemoteConfigValue<T>(\n    val key: String\n) {\n    companion object {\n        val MAZE_QUIZ_GENERATED_QUESTIONS = RemoteConfigValue<Int>(\"maze_quiz_generated_questions\")\n\n        val FLAG_BASE_URL = RemoteConfigValue<String>(\"flag_base_url\")\n\n        val DEFAULT_SHOW_CATEGORY_CONNECTION_INFO = RemoteConfigValue<ShowCategoryConnectionInfo>(\n            key = \"default_show_category_connection_info\"\n        )\n\n        val DAILY_CHALLENGE_TASKS_TO_GENERATE = RemoteConfigValue<Int>(\"daily_challenge_tasks_to_generate\")\n\n        val DAILY_CHALLENGE_ITEM_REWARD = RemoteConfigValue<Int>(\"daily_challenge_item_reward\")\n\n        val COMPARISON_QUIZ_CATEGORIES = RemoteConfigValue<String>(\"comparison_quiz_categories\")\n\n        val COMPARISON_QUIZ_SKIP_COST = RemoteConfigValue<Int>(\"comparison_quiz_skip_cost\")\n\n        val COMPARISON_QUIZ_FIRST_ITEM_HELPER_VALUE = RemoteConfigValue<ComparisonQuizHelperValueState>(\n            key = \"comparison_quiz_first_item_helper_value\"\n        )\n\n        val USER_INITIAL_DIAMONDS = RemoteConfigValue<Int>(\"user_initial_diamonds\")\n\n        val COMPARISON_QUIZ_DEFAULT_XP_REWARD = RemoteConfigValue<Int>(\"comparison_quiz_default_xp_reward\")\n\n        val WORDLE_DEFAULT_XP_REWARD = RemoteConfigValue<Int>(\"wordle_default_xp_reward\")\n\n        val MULTICHOICE_QUIZ_DEFAULT_XP_REWARD = RemoteConfigValue<String>(\"multichoice_quiz_default_xp_reward\")\n\n        val ALL_LOGOS_QUIZ = RemoteConfigValue<String>(\"all_logos_quiz\")\n\n        val MULTICHOICE_QUICKQUIZ_DIFFICULTY = RemoteConfigValue<String>(\"multichoice_quickquiz_difficulty\")\n\n        val MATH_QUIZ_OPERATOR_SIZE = RemoteConfigValue<Int>(\"math_quiz_operator_size\")\n\n        val MATH_QUIZ_DIFFICULTY = RemoteConfigValue<QuestionDifficulty>(\"math_quiz_difficulty\")\n\n        val NEW_LEVEL_DIAMONDS = RemoteConfigValue<Int>(\"new_level_diamonds\")\n\n        val MULTICHOICE_SKIP_COST = RemoteConfigValue<Int>(\"multichoice_skip_cost\")\n    }\n}\n"
  },
  {
    "path": "core/remote-config/src/main/kotlin/com/infinitepower/newquiz/core/remote_config/RemoteConfigXmlParser.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config\n\nimport android.content.Context\nimport android.content.res.XmlResourceParser\nimport android.util.Log\n\n/**\n * Parser for the defaults XML file.\n *\n * @author Miraziz Yusupov\n */\ninternal object RemoteConfigXmlParser {\n    private const val TAG = \"RemoteConfigXmlParser\"\n\n    private const val XML_TAG_ENTRY = \"entry\"\n    private const val XML_TAG_KEY = \"key\"\n    private const val XML_TAG_VALUE = \"value\"\n\n    @Suppress(\"NestedBlockDepth\", \"TooGenericExceptionCaught\")\n    fun parse(\n        context: Context,\n        xmlResId: Int\n    ): Map<String, String> {\n        val defaultsMap = mutableMapOf<String, String>()\n\n        try {\n            val resources = context.resources\n\n            if (resources == null) {\n                Log.e(\n                    TAG,\n                    \"Could not find the resources of the current context while trying to set defaults from an XML.\"\n                )\n                return defaultsMap\n            }\n\n            val xmlParser = resources.getXml(xmlResId)\n\n            var curTag: String? = null\n            var key: String? = null\n            var value: String? = null\n\n            var eventType = xmlParser.eventType\n            while (eventType != XmlResourceParser.END_DOCUMENT) {\n                if (eventType == XmlResourceParser.START_TAG) {\n                    curTag = xmlParser.name\n                } else if (eventType == XmlResourceParser.END_TAG) {\n                    if (xmlParser.name == XML_TAG_ENTRY) {\n                        if (key != null && value != null) {\n                            defaultsMap[key] = value\n                        } else {\n                            Log.w(\n                                TAG,\n                                \"An entry in the defaults XML has an invalid key and/or value tag.\"\n                            )\n                        }\n                        key = null\n                        value = null\n                    }\n                    curTag = null\n                } else if (eventType == XmlResourceParser.TEXT) {\n                    if (curTag != null) {\n                        when (curTag) {\n                            XML_TAG_KEY -> key = xmlParser.text\n                            XML_TAG_VALUE -> value = xmlParser.text\n                            else -> Log.w(\n                                TAG,\n                                \"Encountered an unexpected tag while parsing the defaults XML.\"\n                            )\n                        }\n                    }\n                }\n                eventType = xmlParser.next()\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Error parsing remote config defaults XML\", e)\n        }\n\n        return defaultsMap\n    }\n}\n"
  },
  {
    "path": "core/remote-config/src/main/res/xml/remote_config_defaults.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<defaults>\n  <entry>\n    <key>flag_base_url</key>\n    <value>local</value>\n  </entry>\n  <entry>\n    <key>countries_and_capitals</key>\n    <value>[{\"countryCode\":\"AF\",\"capital\":\"Kabul\",\"difficulty\":\"medium\",\"countryName\":\"Afghanistan\",\"continent\":\"Asia\"},{\"countryCode\":\"AL\",\"capital\":\"Tirana\",\"difficulty\":\"medium\",\"countryName\":\"Albania\",\"continent\":\"Europe\"},{\"countryCode\":\"DZ\",\"capital\":\"Algiers\",\"difficulty\":\"medium\",\"countryName\":\"Algeria\",\"continent\":\"Africa\"},{\"countryCode\":\"AD\",\"capital\":\"Andorra la Vella\",\"difficulty\":\"medium\",\"countryName\":\"Andorra\",\"continent\":\"Europe\"},{\"countryCode\":\"AO\",\"capital\":\"Luanda\",\"difficulty\":\"medium\",\"countryName\":\"Angola\",\"continent\":\"Africa\"},{\"countryCode\":\"AG\",\"capital\":\"St. John's\",\"difficulty\":\"hard\",\"countryName\":\"Antigua and Barbuda\",\"continent\":\"North America\"},{\"countryCode\":\"AR\",\"capital\":\"Buenos Aires\",\"difficulty\":\"medium\",\"countryName\":\"Argentina\",\"continent\":\"South America\"},{\"countryCode\":\"AM\",\"capital\":\"Yerevan\",\"difficulty\":\"medium\",\"countryName\":\"Armenia\",\"continent\":\"Asia\"},{\"countryCode\":\"AU\",\"capital\":\"Canberra\",\"difficulty\":\"medium\",\"countryName\":\"Australia\",\"continent\":\"Oceania\"},{\"countryCode\":\"AT\",\"capital\":\"Vienna\",\"difficulty\":\"easy\",\"countryName\":\"Austria\",\"continent\":\"Europe\"},{\"countryCode\":\"AZ\",\"capital\":\"Baku\",\"difficulty\":\"medium\",\"countryName\":\"Azerbaijan\",\"continent\":\"Asia\"},{\"countryCode\":\"BS\",\"capital\":\"Nassau\",\"difficulty\":\"medium\",\"countryName\":\"Bahamas\",\"continent\":\"North America\"},{\"countryCode\":\"BH\",\"capital\":\"Manama\",\"difficulty\":\"medium\",\"countryName\":\"Bahrain\",\"continent\":\"Asia\"},{\"countryCode\":\"BD\",\"capital\":\"Dhaka\",\"difficulty\":\"medium\",\"countryName\":\"Bangladesh\",\"continent\":\"Asia\"},{\"countryCode\":\"BB\",\"capital\":\"Bridgetown\",\"difficulty\":\"hard\",\"countryName\":\"Barbados\",\"continent\":\"North America\"},{\"countryCode\":\"BY\",\"capital\":\"Minsk\",\"difficulty\":\"medium\",\"countryName\":\"Belarus\",\"continent\":\"Europe\"},{\"countryCode\":\"BE\",\"capital\":\"Brussels\",\"difficulty\":\"easy\",\"countryName\":\"Belgium\",\"continent\":\"Europe\"},{\"countryCode\":\"BZ\",\"capital\":\"Belmopan\",\"difficulty\":\"medium\",\"countryName\":\"Belize\",\"continent\":\"North America\"},{\"countryCode\":\"BJ\",\"capital\":\"Porto-Novo\",\"difficulty\":\"medium\",\"countryName\":\"Benin\",\"continent\":\"Africa\"},{\"countryCode\":\"BT\",\"capital\":\"Thimphu\",\"difficulty\":\"medium\",\"countryName\":\"Bhutan\",\"continent\":\"Asia\"},{\"countryCode\":\"BO\",\"capital\":\"La Paz\",\"difficulty\":\"hard\",\"countryName\":\"Bolivia\",\"continent\":\"South America\"},{\"countryCode\":\"BA\",\"capital\":\"Sarajevo\",\"difficulty\":\"medium\",\"countryName\":\"Bosnia and Herzegovina\",\"continent\":\"Europe\"},{\"countryCode\":\"BW\",\"capital\":\"Gaborone\",\"difficulty\":\"medium\",\"countryName\":\"Botswana\",\"continent\":\"Africa\"},{\"countryCode\":\"BR\",\"capital\":\"Brasília\",\"difficulty\":\"medium\",\"countryName\":\"Brazil\",\"continent\":\"South America\"},{\"countryCode\":\"BN\",\"capital\":\"Bandar Seri Begawan\",\"difficulty\":\"hard\",\"countryName\":\"Brunei\",\"continent\":\"Asia\"},{\"countryCode\":\"BG\",\"capital\":\"Sofia\",\"difficulty\":\"easy\",\"countryName\":\"Bulgaria\",\"continent\":\"Europe\"},{\"countryCode\":\"BF\",\"capital\":\"Ouagadougou\",\"difficulty\":\"medium\",\"countryName\":\"Burkina Faso\",\"continent\":\"Africa\"},{\"countryCode\":\"BI\",\"capital\":\"Bujumbura\",\"difficulty\":\"hard\",\"countryName\":\"Burundi\",\"continent\":\"Africa\"},{\"countryCode\":\"KH\",\"capital\":\"Phnom Penh\",\"difficulty\":\"medium\",\"countryName\":\"Cambodia\",\"continent\":\"Asia\"},{\"countryCode\":\"CM\",\"capital\":\"Yaoundé\",\"difficulty\":\"medium\",\"countryName\":\"Cameroon\",\"continent\":\"Africa\"},{\"countryCode\":\"CA\",\"capital\":\"Ottawa\",\"difficulty\":\"easy\",\"countryName\":\"Canada\",\"continent\":\"North America\"},{\"countryCode\":\"CV\",\"capital\":\"Praia\",\"difficulty\":\"medium\",\"countryName\":\"Cape Verde\",\"continent\":\"Africa\"},{\"countryCode\":\"CF\",\"capital\":\"Bangui\",\"difficulty\":\"medium\",\"countryName\":\"Central African Republic\",\"continent\":\"Africa\"},{\"countryCode\":\"TD\",\"capital\":\"N'Djamena\",\"difficulty\":\"medium\",\"countryName\":\"Chad\",\"continent\":\"Africa\"},{\"countryCode\":\"CL\",\"capital\":\"Santiago\",\"difficulty\":\"medium\",\"countryName\":\"Chile\",\"continent\":\"South America\"},{\"countryCode\":\"CN\",\"capital\":\"Beijing\",\"difficulty\":\"medium\",\"countryName\":\"China\",\"continent\":\"Asia\"},{\"countryCode\":\"CO\",\"capital\":\"Bogotá\",\"difficulty\":\"medium\",\"countryName\":\"Colombia\",\"continent\":\"South America\"},{\"countryCode\":\"KM\",\"capital\":\"Moroni\",\"difficulty\":\"medium\",\"countryName\":\"Comoros\",\"continent\":\"Africa\"},{\"countryCode\":\"CD\",\"capital\":\"Kinshasa\",\"difficulty\":\"medium\",\"countryName\":\"Democratic Republic of the Congo\",\"continent\":\"Africa\"},{\"countryCode\":\"CG\",\"capital\":\"Brazzaville\",\"difficulty\":\"medium\",\"countryName\":\"Republic of the Congo\",\"continent\":\"Africa\"},{\"countryCode\":\"CR\",\"capital\":\"San José\",\"difficulty\":\"medium\",\"countryName\":\"Costa Rica\",\"continent\":\"North America\"},{\"countryCode\":\"CI\",\"capital\":\"Yamoussoukro\",\"difficulty\":\"medium\",\"countryName\":\"Côte d'Ivoire\",\"continent\":\"Africa\"},{\"countryCode\":\"HR\",\"capital\":\"Zagreb\",\"difficulty\":\"medium\",\"countryName\":\"Croatia\",\"continent\":\"Europe\"},{\"countryCode\":\"CU\",\"capital\":\"Havana\",\"difficulty\":\"medium\",\"countryName\":\"Cuba\",\"continent\":\"North America\"},{\"countryCode\":\"CY\",\"capital\":\"Nicosia\",\"difficulty\":\"medium\",\"countryName\":\"Cyprus\",\"continent\":\"Asia\"},{\"countryCode\":\"CZ\",\"capital\":\"Prague\",\"difficulty\":\"medium\",\"countryName\":\"Czech Republic\",\"continent\":\"Europe\"},{\"countryCode\":\"DK\",\"capital\":\"Copenhagen\",\"difficulty\":\"easy\",\"countryName\":\"Denmark\",\"continent\":\"Europe\"},{\"countryCode\":\"DJ\",\"capital\":\"Djibouti\",\"difficulty\":\"hard\",\"countryName\":\"Djibouti\",\"continent\":\"Africa\"},{\"countryCode\":\"DM\",\"capital\":\"Roseau\",\"difficulty\":\"medium\",\"countryName\":\"Dominica\",\"continent\":\"North America\"},{\"countryCode\":\"DO\",\"capital\":\"Santo Domingo\",\"difficulty\":\"medium\",\"countryName\":\"Dominican Republic\",\"continent\":\"North America\"},{\"countryCode\":\"EC\",\"capital\":\"Quito\",\"difficulty\":\"medium\",\"countryName\":\"Ecuador\",\"continent\":\"South America\"},{\"countryCode\":\"EG\",\"capital\":\"Cairo\",\"difficulty\":\"medium\",\"countryName\":\"Egypt\",\"continent\":\"Africa\"},{\"countryCode\":\"SV\",\"capital\":\"San Salvador\",\"difficulty\":\"medium\",\"countryName\":\"El Salvador\",\"continent\":\"North America\"},{\"countryCode\":\"GQ\",\"capital\":\"Malabo\",\"difficulty\":\"medium\",\"countryName\":\"Equatorial Guinea\",\"continent\":\"Africa\"},{\"countryCode\":\"ER\",\"capital\":\"Asmara\",\"difficulty\":\"hard\",\"countryName\":\"Eritrea\",\"continent\":\"Africa\"},{\"countryCode\":\"EE\",\"capital\":\"Tallinn\",\"difficulty\":\"medium\",\"countryName\":\"Estonia\",\"continent\":\"Europe\"},{\"countryCode\":\"ET\",\"capital\":\"Addis Ababa\",\"difficulty\":\"hard\",\"countryName\":\"Ethiopia\",\"continent\":\"Africa\"},{\"countryCode\":\"FJ\",\"capital\":\"Suva\",\"difficulty\":\"medium\",\"countryName\":\"Fiji\",\"continent\":\"Oceania\"},{\"countryCode\":\"FI\",\"capital\":\"Helsinki\",\"difficulty\":\"medium\",\"countryName\":\"Finland\",\"continent\":\"Europe\"},{\"countryCode\":\"FR\",\"capital\":\"Paris\",\"difficulty\":\"medium\",\"countryName\":\"France\",\"continent\":\"Europe\"},{\"countryCode\":\"GA\",\"capital\":\"Libreville\",\"difficulty\":\"medium\",\"countryName\":\"Gabon\",\"continent\":\"Africa\"},{\"countryCode\":\"GM\",\"capital\":\"Banjul\",\"difficulty\":\"medium\",\"countryName\":\"Gambia\",\"continent\":\"Africa\"},{\"countryCode\":\"GE\",\"capital\":\"Tbilisi\",\"difficulty\":\"medium\",\"countryName\":\"Georgia\",\"continent\":\"Asia\"},{\"countryCode\":\"DE\",\"capital\":\"Berlin\",\"difficulty\":\"medium\",\"countryName\":\"Germany\",\"continent\":\"Europe\"},{\"countryCode\":\"GH\",\"capital\":\"Accra\",\"difficulty\":\"medium\",\"countryName\":\"Ghana\",\"continent\":\"Africa\"},{\"countryCode\":\"GR\",\"capital\":\"Athens\",\"difficulty\":\"medium\",\"countryName\":\"Greece\",\"continent\":\"Europe\"},{\"countryCode\":\"GD\",\"capital\":\"St. George's\",\"difficulty\":\"medium\",\"countryName\":\"Grenada\",\"continent\":\"North America\"},{\"countryCode\":\"GT\",\"capital\":\"Guatemala City\",\"difficulty\":\"medium\",\"countryName\":\"Guatemala\",\"continent\":\"North America\"},{\"countryCode\":\"GN\",\"capital\":\"Conakry\",\"difficulty\":\"medium\",\"countryName\":\"Guinea\",\"continent\":\"Africa\"},{\"countryCode\":\"GW\",\"capital\":\"Bissau\",\"difficulty\":\"medium\",\"countryName\":\"Guinea-Bissau\",\"continent\":\"Africa\"},{\"countryCode\":\"GY\",\"capital\":\"Georgetown\",\"difficulty\":\"medium\",\"countryName\":\"Guyana\",\"continent\":\"South America\"},{\"countryCode\":\"HT\",\"capital\":\"Port-au-Prince\",\"difficulty\":\"medium\",\"countryName\":\"Haiti\",\"continent\":\"North America\"},{\"countryCode\":\"HN\",\"capital\":\"Tegucigalpa\",\"difficulty\":\"medium\",\"countryName\":\"Honduras\",\"continent\":\"North America\"},{\"countryCode\":\"HU\",\"capital\":\"Budapest\",\"difficulty\":\"medium\",\"countryName\":\"Hungary\",\"continent\":\"Europe\"},{\"countryCode\":\"IS\",\"capital\":\"Reykjavik\",\"difficulty\":\"medium\",\"countryName\":\"Iceland\",\"continent\":\"Europe\"},{\"countryCode\":\"IN\",\"capital\":\"New Delhi\",\"difficulty\":\"medium\",\"countryName\":\"India\",\"continent\":\"Asia\"},{\"countryCode\":\"ID\",\"capital\":\"Jakarta\",\"difficulty\":\"medium\",\"countryName\":\"Indonesia\",\"continent\":\"Asia\"},{\"countryCode\":\"IR\",\"capital\":\"Tehran\",\"difficulty\":\"hard\",\"countryName\":\"Iran\",\"continent\":\"Asia\"},{\"countryCode\":\"IQ\",\"capital\":\"Baghdad\",\"difficulty\":\"hard\",\"countryName\":\"Iraq\",\"continent\":\"Asia\"},{\"countryCode\":\"IE\",\"capital\":\"Dublin\",\"difficulty\":\"medium\",\"countryName\":\"Ireland\",\"continent\":\"Europe\"},{\"countryCode\":\"IL\",\"capital\":\"Jerusalem\",\"difficulty\":\"medium\",\"countryName\":\"Israel\",\"continent\":\"Asia\"},{\"countryCode\":\"IT\",\"capital\":\"Rome\",\"difficulty\":\"medium\",\"countryName\":\"Italy\",\"continent\":\"Europe\"},{\"countryCode\":\"JM\",\"capital\":\"Kingston\",\"difficulty\":\"medium\",\"countryName\":\"Jamaica\",\"continent\":\"North America\"},{\"countryCode\":\"JP\",\"capital\":\"Tokyo\",\"difficulty\":\"medium\",\"countryName\":\"Japan\",\"continent\":\"Asia\"},{\"countryCode\":\"JO\",\"capital\":\"Amman\",\"difficulty\":\"medium\",\"countryName\":\"Jordan\",\"continent\":\"Asia\"},{\"countryCode\":\"KZ\",\"capital\":\"Nur-Sultan\",\"difficulty\":\"medium\",\"countryName\":\"Kazakhstan\",\"continent\":\"Asia\"},{\"countryCode\":\"KE\",\"capital\":\"Nairobi\",\"difficulty\":\"medium\",\"countryName\":\"Kenya\",\"continent\":\"Africa\"},{\"countryCode\":\"KI\",\"capital\":\"Tarawa\",\"difficulty\":\"medium\",\"countryName\":\"Kiribati\",\"continent\":\"Oceania\"},{\"countryCode\":\"KP\",\"capital\":\"Pyongyang\",\"difficulty\":\"hard\",\"countryName\":\"North Korea\",\"continent\":\"Asia\"},{\"countryCode\":\"KR\",\"capital\":\"Seoul\",\"difficulty\":\"medium\",\"countryName\":\"South Korea\",\"continent\":\"Asia\"},{\"countryCode\":\"KW\",\"capital\":\"Kuwait City\",\"difficulty\":\"medium\",\"countryName\":\"Kuwait\",\"continent\":\"Asia\"},{\"countryCode\":\"KG\",\"capital\":\"Bishkek\",\"difficulty\":\"medium\",\"countryName\":\"Kyrgyzstan\",\"continent\":\"Asia\"},{\"countryCode\":\"LA\",\"capital\":\"Vientiane\",\"difficulty\":\"hard\",\"countryName\":\"Laos\",\"continent\":\"Asia\"},{\"countryCode\":\"LV\",\"capital\":\"Riga\",\"difficulty\":\"medium\",\"countryName\":\"Latvia\",\"continent\":\"Europe\"},{\"countryCode\":\"LB\",\"capital\":\"Beirut\",\"difficulty\":\"medium\",\"countryName\":\"Lebanon\",\"continent\":\"Asia\"},{\"countryCode\":\"LS\",\"capital\":\"Maseru\",\"difficulty\":\"medium\",\"countryName\":\"Lesotho\",\"continent\":\"Africa\"},{\"countryCode\":\"LR\",\"capital\":\"Monrovia\",\"difficulty\":\"medium\",\"countryName\":\"Liberia\",\"continent\":\"Africa\"},{\"countryCode\":\"LY\",\"capital\":\"Tripoli\",\"difficulty\":\"hard\",\"countryName\":\"Libya\",\"continent\":\"Africa\"},{\"countryCode\":\"LI\",\"capital\":\"Vaduz\",\"difficulty\":\"medium\",\"countryName\":\"Liechtenstein\",\"continent\":\"Europe\"},{\"countryCode\":\"LT\",\"capital\":\"Vilnius\",\"difficulty\":\"medium\",\"countryName\":\"Lithuania\",\"continent\":\"Europe\"},{\"countryCode\":\"LU\",\"capital\":\"Luxembourg City\",\"difficulty\":\"medium\",\"countryName\":\"Luxembourg\",\"continent\":\"Europe\"},{\"countryCode\":\"MG\",\"capital\":\"Antananarivo\",\"difficulty\":\"medium\",\"countryName\":\"Madagascar\",\"continent\":\"Africa\"},{\"countryCode\":\"MW\",\"capital\":\"Lilongwe\",\"difficulty\":\"medium\",\"countryName\":\"Malawi\",\"continent\":\"Africa\"},{\"countryCode\":\"MY\",\"capital\":\"Kuala Lumpur\",\"difficulty\":\"medium\",\"countryName\":\"Malaysia\",\"continent\":\"Asia\"},{\"countryCode\":\"MV\",\"capital\":\"Male\",\"difficulty\":\"medium\",\"countryName\":\"Maldives\",\"continent\":\"Asia\"},{\"countryCode\":\"ML\",\"capital\":\"Bamako\",\"difficulty\":\"medium\",\"countryName\":\"Mali\",\"continent\":\"Africa\"},{\"countryCode\":\"MT\",\"capital\":\"Valletta\",\"difficulty\":\"medium\",\"countryName\":\"Malta\",\"continent\":\"Europe\"},{\"countryCode\":\"MH\",\"capital\":\"Majuro\",\"difficulty\":\"medium\",\"countryName\":\"Marshall Islands\",\"continent\":\"Oceania\"},{\"countryCode\":\"MR\",\"capital\":\"Nouakchott\",\"difficulty\":\"medium\",\"countryName\":\"Mauritania\",\"continent\":\"Africa\"},{\"countryCode\":\"MU\",\"capital\":\"Port Louis\",\"difficulty\":\"medium\",\"countryName\":\"Mauritius\",\"continent\":\"Africa\"},{\"countryCode\":\"MX\",\"capital\":\"Mexico City\",\"difficulty\":\"medium\",\"countryName\":\"Mexico\",\"continent\":\"North America\"},{\"countryCode\":\"FM\",\"capital\":\"Palikir\",\"difficulty\":\"medium\",\"countryName\":\"Micronesia\",\"continent\":\"Oceania\"},{\"countryCode\":\"MD\",\"capital\":\"Chisinau\",\"difficulty\":\"medium\",\"countryName\":\"Moldova\",\"continent\":\"Europe\"},{\"countryCode\":\"MC\",\"capital\":\"Monaco\",\"difficulty\":\"medium\",\"countryName\":\"Monaco\",\"continent\":\"Europe\"},{\"countryCode\":\"MN\",\"capital\":\"Ulaanbaatar\",\"difficulty\":\"medium\",\"countryName\":\"Mongolia\",\"continent\":\"Asia\"},{\"countryCode\":\"ME\",\"capital\":\"Podgorica\",\"difficulty\":\"medium\",\"countryName\":\"Montenegro\",\"continent\":\"Europe\"},{\"countryCode\":\"MA\",\"capital\":\"Rabat\",\"difficulty\":\"medium\",\"countryName\":\"Morocco\",\"continent\":\"Africa\"},{\"countryCode\":\"MZ\",\"capital\":\"Maputo\",\"difficulty\":\"medium\",\"countryName\":\"Mozambique\",\"continent\":\"Africa\"},{\"countryCode\":\"MM\",\"capital\":\"Naypyidaw\",\"difficulty\":\"medium\",\"countryName\":\"Myanmar (Burma)\",\"continent\":\"Asia\"},{\"countryCode\":\"NA\",\"capital\":\"Windhoek\",\"difficulty\":\"medium\",\"countryName\":\"Namibia\",\"continent\":\"Africa\"},{\"countryCode\":\"NR\",\"capital\":\"Yaren\",\"difficulty\":\"hard\",\"countryName\":\"Nauru\",\"continent\":\"Oceania\"},{\"countryCode\":\"NP\",\"capital\":\"Kathmandu\",\"difficulty\":\"medium\",\"countryName\":\"Nepal\",\"continent\":\"Asia\"},{\"countryCode\":\"NL\",\"capital\":\"Amsterdam\",\"difficulty\":\"medium\",\"countryName\":\"Netherlands\",\"continent\":\"Europe\"},{\"countryCode\":\"NZ\",\"capital\":\"Wellington\",\"difficulty\":\"medium\",\"countryName\":\"New Zealand\",\"continent\":\"Oceania\"},{\"countryCode\":\"NI\",\"capital\":\"Managua\",\"difficulty\":\"medium\",\"countryName\":\"Nicaragua\",\"continent\":\"North America\"},{\"countryCode\":\"NE\",\"capital\":\"Niamey\",\"difficulty\":\"medium\",\"countryName\":\"Niger\",\"continent\":\"Africa\"},{\"countryCode\":\"NG\",\"capital\":\"Abuja\",\"difficulty\":\"medium\",\"countryName\":\"Nigeria\",\"continent\":\"Africa\"},{\"countryCode\":\"KP\",\"capital\":\"Pyongyang\",\"difficulty\":\"hard\",\"countryName\":\"North Korea\",\"continent\":\"Asia\"},{\"countryCode\":\"MK\",\"capital\":\"Skopje\",\"difficulty\":\"medium\",\"countryName\":\"North Macedonia\",\"continent\":\"Europe\"},{\"countryCode\":\"NO\",\"capital\":\"Oslo\",\"difficulty\":\"medium\",\"countryName\":\"Norway\",\"continent\":\"Europe\"},{\"countryCode\":\"OM\",\"capital\":\"Muscat\",\"difficulty\":\"medium\",\"countryName\":\"Oman\",\"continent\":\"Asia\"},{\"countryCode\":\"PK\",\"capital\":\"Islamabad\",\"difficulty\":\"medium\",\"countryName\":\"Pakistan\",\"continent\":\"Asia\"},{\"countryCode\":\"PW\",\"capital\":\"Ngerulmud\",\"difficulty\":\"hard\",\"countryName\":\"Palau\",\"continent\":\"Oceania\"},{\"countryCode\":\"PA\",\"capital\":\"Panama City\",\"difficulty\":\"medium\",\"countryName\":\"Panama\",\"continent\":\"North America\"},{\"countryCode\":\"PG\",\"capital\":\"Port Moresby\",\"difficulty\":\"medium\",\"countryName\":\"Papua New Guinea\",\"continent\":\"Oceania\"},{\"countryCode\":\"PY\",\"capital\":\"Asunción\",\"difficulty\":\"medium\",\"countryName\":\"Paraguay\",\"continent\":\"South America\"},{\"countryCode\":\"PE\",\"capital\":\"Lima\",\"difficulty\":\"medium\",\"countryName\":\"Peru\",\"continent\":\"South America\"},{\"countryCode\":\"PH\",\"capital\":\"Manila\",\"difficulty\":\"medium\",\"countryName\":\"Philippines\",\"continent\":\"Asia\"},{\"countryCode\":\"PL\",\"capital\":\"Warsaw\",\"difficulty\":\"medium\",\"countryName\":\"Poland\",\"continent\":\"Europe\"},{\"countryCode\":\"PT\",\"capital\":\"Lisbon\",\"difficulty\":\"medium\",\"countryName\":\"Portugal\",\"continent\":\"Europe\"},{\"countryCode\":\"QA\",\"capital\":\"Doha\",\"difficulty\":\"medium\",\"countryName\":\"Qatar\",\"continent\":\"Asia\"},{\"countryCode\":\"RO\",\"capital\":\"Bucharest\",\"difficulty\":\"medium\",\"countryName\":\"Romania\",\"continent\":\"Europe\"},{\"countryCode\":\"RU\",\"capital\":\"Moscow\",\"difficulty\":\"hard\",\"countryName\":\"Russia\",\"continent\":\"Europe\"},{\"countryCode\":\"RW\",\"capital\":\"Kigali\",\"difficulty\":\"medium\",\"countryName\":\"Rwanda\",\"continent\":\"Africa\"},{\"countryCode\":\"KN\",\"capital\":\"Basseterre\",\"difficulty\":\"medium\",\"countryName\":\"Saint Kitts and Nevis\",\"continent\":\"North America\"},{\"countryCode\":\"LC\",\"capital\":\"Castries\",\"difficulty\":\"medium\",\"countryName\":\"Saint Lucia\",\"continent\":\"North America\"},{\"countryCode\":\"VC\",\"capital\":\"Kingstown\",\"difficulty\":\"medium\",\"countryName\":\"Saint Vincent and the Grenadines\",\"continent\":\"North America\"},{\"countryCode\":\"WS\",\"capital\":\"Apia\",\"difficulty\":\"medium\",\"countryName\":\"Samoa\",\"continent\":\"Oceania\"},{\"countryCode\":\"SM\",\"capital\":\"San Marino\",\"difficulty\":\"hard\",\"countryName\":\"San Marino\",\"continent\":\"Europe\"},{\"countryCode\":\"ST\",\"capital\":\"São Tomé\",\"difficulty\":\"medium\",\"countryName\":\"Sao Tome and Principe\",\"continent\":\"Africa\"},{\"countryCode\":\"SA\",\"capital\":\"Riyadh\",\"difficulty\":\"medium\",\"countryName\":\"Saudi Arabia\",\"continent\":\"Asia\"},{\"countryCode\":\"SN\",\"capital\":\"Dakar\",\"difficulty\":\"medium\",\"countryName\":\"Senegal\",\"continent\":\"Africa\"},{\"countryCode\":\"RS\",\"capital\":\"Belgrade\",\"difficulty\":\"medium\",\"countryName\":\"Serbia\",\"continent\":\"Europe\"},{\"countryCode\":\"SC\",\"capital\":\"Victoria\",\"difficulty\":\"medium\",\"countryName\":\"Seychelles\",\"continent\":\"Africa\"},{\"countryCode\":\"SL\",\"capital\":\"Freetown\",\"difficulty\":\"medium\",\"countryName\":\"Sierra Leone\",\"continent\":\"Africa\"},{\"countryCode\":\"SG\",\"capital\":\"Singapore\",\"difficulty\":\"medium\",\"countryName\":\"Singapore\",\"continent\":\"Asia\"},{\"countryCode\":\"SK\",\"capital\":\"Bratislava\",\"difficulty\":\"medium\",\"countryName\":\"Slovakia\",\"continent\":\"Europe\"},{\"countryCode\":\"SI\",\"capital\":\"Ljubljana\",\"difficulty\":\"medium\",\"countryName\":\"Slovenia\",\"continent\":\"Europe\"},{\"countryCode\":\"SB\",\"capital\":\"Honiara\",\"difficulty\":\"medium\",\"countryName\":\"Solomon Islands\",\"continent\":\"Oceania\"},{\"countryCode\":\"SO\",\"capital\":\"Mogadishu\",\"difficulty\":\"medium\",\"countryName\":\"Somalia\",\"continent\":\"Africa\"},{\"countryCode\":\"ZA\",\"capital\":\"Pretoria\",\"difficulty\":\"medium\",\"countryName\":\"South Africa\",\"continent\":\"Africa\"},{\"countryCode\":\"KR\",\"capital\":\"Seoul\",\"difficulty\":\"medium\",\"countryName\":\"South Korea\",\"continent\":\"Asia\"},{\"countryCode\":\"SS\",\"capital\":\"Juba\",\"difficulty\":\"medium\",\"countryName\":\"South Sudan\",\"continent\":\"Africa\"},{\"countryCode\":\"ES\",\"capital\":\"Madrid\",\"difficulty\":\"medium\",\"countryName\":\"Spain\",\"continent\":\"Europe\"},{\"countryCode\":\"LK\",\"capital\":\"Sri Jayawardenepura Kotte\",\"difficulty\":\"medium\",\"countryName\":\"Sri Lanka\",\"continent\":\"Asia\"},{\"countryCode\":\"SD\",\"capital\":\"Khartoum\",\"difficulty\":\"medium\",\"countryName\":\"Sudan\",\"continent\":\"Africa\"},{\"countryCode\":\"SR\",\"capital\":\"Paramaribo\",\"difficulty\":\"medium\",\"countryName\":\"Suriname\",\"continent\":\"South America\"},{\"countryCode\":\"SZ\",\"capital\":\"Mbabane\",\"difficulty\":\"medium\",\"countryName\":\"Eswatini\",\"continent\":\"Africa\"},{\"countryCode\":\"SE\",\"capital\":\"Stockholm\",\"difficulty\":\"medium\",\"countryName\":\"Sweden\",\"continent\":\"Europe\"},{\"countryCode\":\"CH\",\"capital\":\"Bern\",\"difficulty\":\"medium\",\"countryName\":\"Switzerland\",\"continent\":\"Europe\"},{\"countryCode\":\"SY\",\"capital\":\"Damascus\",\"difficulty\":\"medium\",\"countryName\":\"Syria\",\"continent\":\"Asia\"},{\"countryCode\":\"TW\",\"capital\":\"Taipei\",\"difficulty\":\"medium\",\"countryName\":\"Taiwan\",\"continent\":\"Asia\"},{\"countryCode\":\"TJ\",\"capital\":\"Dushanbe\",\"difficulty\":\"medium\",\"countryName\":\"Tajikistan\",\"continent\":\"Asia\"},{\"countryCode\":\"TZ\",\"capital\":\"Dodoma\",\"difficulty\":\"medium\",\"countryName\":\"Tanzania\",\"continent\":\"Africa\"},{\"countryCode\":\"TH\",\"capital\":\"Bangkok\",\"difficulty\":\"medium\",\"countryName\":\"Thailand\",\"continent\":\"Asia\"},{\"countryCode\":\"TL\",\"capital\":\"Dili\",\"difficulty\":\"medium\",\"countryName\":\"Timor-Leste\",\"continent\":\"Asia\"},{\"countryCode\":\"TG\",\"capital\":\"Lomé\",\"difficulty\":\"medium\",\"countryName\":\"Togo\",\"continent\":\"Africa\"},{\"countryCode\":\"TO\",\"capital\":\"Nuku'alofa\",\"difficulty\":\"medium\",\"countryName\":\"Tonga\",\"continent\":\"Oceania\"},{\"countryCode\":\"TT\",\"capital\":\"Port of Spain\",\"difficulty\":\"medium\",\"countryName\":\"Trinidad and Tobago\",\"continent\":\"North America\"},{\"countryCode\":\"TN\",\"capital\":\"Tunis\",\"difficulty\":\"medium\",\"countryName\":\"Tunisia\",\"continent\":\"Africa\"},{\"countryCode\":\"TR\",\"capital\":\"Ankara\",\"difficulty\":\"medium\",\"countryName\":\"Turkey\",\"continent\":\"Asia\"},{\"countryCode\":\"TM\",\"capital\":\"Ashgabat\",\"difficulty\":\"medium\",\"countryName\":\"Turkmenistan\",\"continent\":\"Asia\"},{\"countryCode\":\"TV\",\"capital\":\"Funafuti\",\"difficulty\":\"medium\",\"countryName\":\"Tuvalu\",\"continent\":\"Oceania\"},{\"countryCode\":\"UG\",\"capital\":\"Kampala\",\"difficulty\":\"medium\",\"countryName\":\"Uganda\",\"continent\":\"Africa\"},{\"countryCode\":\"UA\",\"capital\":\"Kyiv\",\"difficulty\":\"medium\",\"countryName\":\"Ukraine\",\"continent\":\"Europe\"},{\"countryCode\":\"AE\",\"capital\":\"Abu Dhabi\",\"difficulty\":\"medium\",\"countryName\":\"United Arab Emirates\",\"continent\":\"Asia\"},{\"countryCode\":\"GB\",\"capital\":\"London\",\"difficulty\":\"medium\",\"countryName\":\"United Kingdom\",\"continent\":\"Europe\"},{\"countryCode\":\"US\",\"capital\":\"Washington D.C.\",\"difficulty\":\"medium\",\"countryName\":\"United States of America\",\"continent\":\"North America\"},{\"countryCode\":\"UY\",\"capital\":\"Montevideo\",\"difficulty\":\"medium\",\"countryName\":\"Uruguay\",\"continent\":\"South America\"},{\"countryCode\":\"UZ\",\"capital\":\"Tashkent\",\"difficulty\":\"medium\",\"countryName\":\"Uzbekistan\",\"continent\":\"Asia\"},{\"countryCode\":\"VU\",\"capital\":\"Port Vila\",\"difficulty\":\"medium\",\"countryName\":\"Vanuatu\",\"continent\":\"Oceania\"},{\"countryCode\":\"VE\",\"capital\":\"Caracas\",\"difficulty\":\"medium\",\"countryName\":\"Venezuela\",\"continent\":\"South America\"},{\"countryCode\":\"VN\",\"capital\":\"Hanoi\",\"difficulty\":\"medium\",\"countryName\":\"Vietnam\",\"continent\":\"Asia\"},{\"countryCode\":\"EH\",\"capital\":\"El Aaiún\",\"difficulty\":\"hard\",\"countryName\":\"Western Sahara\",\"continent\":\"Africa\"},{\"countryCode\":\"YE\",\"capital\":\"Sana'a\",\"difficulty\":\"medium\",\"countryName\":\"Yemen\",\"continent\":\"Asia\"},{\"countryCode\":\"ZM\",\"capital\":\"Lusaka\",\"difficulty\":\"medium\",\"countryName\":\"Zambia\",\"continent\":\"Africa\"},{\"countryCode\":\"ZW\",\"capital\":\"Harare\",\"difficulty\":\"medium\",\"countryName\":\"Zimbabwe\",\"continent\":\"Africa\"}]</value>\n  </entry>\n  <entry>\n    <key>maze_quiz_generated_questions</key>\n    <value>50</value>\n  </entry>\n  <entry>\n    <key>default_show_category_connection_info</key>\n    <value>NONE</value>\n  </entry>\n  <entry>\n    <key>daily_challenge_tasks_to_generate</key>\n    <value>5</value>\n  </entry>\n  <entry>\n    <key>daily_challenge_item_reward</key>\n    <value>1</value>\n  </entry>\n  <entry>\n    <key>comparison_quiz_categories</key>\n    <value>[{\"id\":\"country-population\",\"name\":\"Country Population\",\"description\":\"Compare population of each country\",\"image\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fworld_population_illustration.png?alt=media&amp;token=db98d82c-7bbb-41bc-94a8-16771e0699aa\",\"requireInternetConnection\":\"false\",\"generateQuestionsLocally\":\"true\",\"questionDescription\":{\"greater\":\"Which country has more population?\",\"less\":\"Which country has less population?\"},\"formatType\":\"default\",\"dataSourceAttribution\":{\"text\":\"Data from RESTCountries\"}},{\"id\":\"country-area\",\"name\":\"Country Area\",\"description\":\"Compare the area of each country (km²)\",\"image\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fflags_illustration.png?alt=media&amp;token=ec6b2820-1d26-4352-9c54-201bd387ae94\",\"requireInternetConnection\":\"false\",\"generateQuestionsLocally\":\"true\",\"questionDescription\":{\"greater\":\"Which country has more area?\",\"less\":\"Which country has less area?\"},\"formatType\":\"distance\",\"helperValueSuffix\":\"square_kilometer\",\"dataSourceAttribution\":{\"text\":\"Data from RESTCountries\"}},{\"id\":\"movie-popularity\",\"name\":\"Movie Popularity\",\"description\":\"Compare the populatiry of movies.\",\"image\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fmovie_popularity_illustration.png?alt=media&amp;token=9e54b03f-391a-4cd9-9a04-e0a8f29d0b9f\",\"questionDescription\":{\"greater\":\"Which movie is more popular?\",\"less\":\"Which movie is less popular?\"},\"formatType\":\"default\",\"dataSourceAttribution\":{\"text\":\"Powered by TMDB\"}},{\"id\":\"movie-release-date\",\"name\":\"Movie Release Date\",\"description\":\"Compare the release date of movies.\",\"image\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fmovie_popularity_illustration.png?alt=media&amp;token=9e54b03f-391a-4cd9-9a04-e0a8f29d0b9f\",\"questionDescription\":{\"greater\":\"Which movie is more recent?\",\"less\":\"Which movie is less recent?\"},\"formatType\":\"date\",\"dataSourceAttribution\":{\"text\":\"Powered by TMDB\"}},{\"id\":\"club-trophies\",\"name\":\"Club Trophies\",\"description\":\"Compare trophies of clubs.\",\"image\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/comparison-quiz%2Fclub-trophies.jpg?alt=media&amp;token=532f1a0b-ce22-4fd2-9d02-6ff7e480a08d\",\"questionDescription\":{\"greater\":\"Which club has more trophies?\",\"less\":\"Which club has less trophies?\"},\"formatType\":\"default\"},{\"id\":\"club-foundation-date\",\"name\":\"Club Foundation Date\",\"description\":\"Compare foundation date of clubs.\",\"image\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/comparison-quiz%2Fstadium-field.jpg?alt=media&amp;token=bd9b32b6-0cd3-4a4b-bed5-c3ace9a7734c\",\"questionDescription\":{\"greater\":\"Which club is more recent?\",\"less\":\"Which club is older?\"},\"formatType\":\"date\"},{\"id\":\"club-stadium-capacity\",\"name\":\"Stadium Capacity\",\"description\":\"Compare the capacity of stadiums.\",\"image\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/comparison-quiz%2Fstadium-inside.jpg?alt=media&amp;token=82843706-2fab-4f12-811a-1f556a0c0674\",\"questionDescription\":{\"greater\":\"Which stadium has more capacity?\",\"less\":\"Which stadium has less capacity?\"},\"formatType\":\"default\"},{\"id\":\"club-stadium-opened-date\",\"name\":\"Stadium Opening Date\",\"description\":\"Compare the opening date of stadiums.\",\"image\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/comparison-quiz%2Fstadium-inside2.jpg?alt=media&amp;token=38771c7d-1a4b-47a9-bb89-f49f2fc3ad95\",\"questionDescription\":{\"greater\":\"Which stadium opened most recently?\",\"less\":\"Which stadium opened first?\"},\"formatType\":\"date\"}]</value>\n  </entry>\n  <entry>\n    <key>comparison_quiz_skip_cost</key>\n    <value>1</value>\n  </entry>\n  <entry>\n    <key>comparison_quiz_first_item_helper_value</key>\n    <value>HIDDEN</value>\n  </entry>\n  <entry>\n    <key>user_initial_diamonds</key>\n    <value>10</value>\n  </entry>\n  <entry>\n    <key>new_level_diamonds</key>\n    <value>10</value>\n  </entry>\n  <entry>\n    <key>comparison_quiz_default_xp_reward</key>\n    <value>5</value>\n  </entry>\n  <entry>\n    <key>wordle_default_xp_reward</key>\n    <value>50</value>\n  </entry>\n  <entry>\n    <key>multichoice_quiz_default_xp_reward</key>\n    <value>{\"easy\":10,\"medium\":15,\"hard\":20}</value>\n  </entry>\n  <entry>\n    <key>all_logos_quiz</key>\n    <value>[{\"description\":\"What is the name of this game?\",\"name\":\"New Quiz\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoNewQuiz.png?alt=media&amp;token=239af120-55a6-480d-beea-279da9133e45\",\"incorrect_answers\":[\"New Social\",\"Mega Quiz\",\"My Quiz\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this social app?\",\"name\":\"Instagram\",\"img_url\":\"https://play-lh.googleusercontent.com/LM9vBt64KdRxLFRPMpNM6OvnGTGoUFSXYV-w-cGVeUxhgFWkCsfsPSJ5GYh7x9qKqw=s300\",\"incorrect_answers\":[\"Facebook\",\"Whats App\",\"Snap Chat\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this social app?\",\"name\":\"Discord\",\"img_url\":\"https://play-lh.googleusercontent.com/0oO5sAneb9lJP6l8c6DH4aj6f85qNpplQVHmPmbbBxAukDnlO7DarDW0b-kEIHa8SQ=s300\",\"incorrect_answers\":[\"Facebook\",\"Whats App\",\"Instagram\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this social app?\",\"name\":\"Facebook\",\"img_url\":\"https://play-lh.googleusercontent.com/ccWDU4A7fX1R24v-vvT480ySh26AYp97g1VrIB_FIdjRcuQB2JP2WdY7h_wVVAeSpg=s300\",\"incorrect_answers\":[\"Instagram\",\"Whats App\",\"Face Time\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this company?\",\"name\":\"Google\",\"img_url\":\"https://kgo.googleusercontent.com/profile_vrt_raw_bytes_1587515358_10512.png\",\"incorrect_answers\":[\"Meta\",\"Apple\",\"Microsoft\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this company?\",\"name\":\"Starbucks\",\"img_url\":\"https://static.dicionariodesimbolos.com.br/upload/d1/42/logo-da-starbucks-significado-historia-e-evolucao-4_xl.jpeg\",\"incorrect_answers\":[\"Starcoffee\",\"McDonald's\",\"Startasks\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this fast food company?\",\"name\":\"McDonald's\",\"img_url\":\"https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/McDonald%27s_Golden_Arches.svg/1200px-McDonald%27s_Golden_Arches.svg.png\",\"incorrect_answers\":[\"Burger King\",\"MyDonald's\",\"McBurger\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this company?\",\"name\":\"IKEA\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoQuiz%2Fikea_clear.png?alt=media&amp;token=74728d10-7e33-4e8c-8ab9-c79a6abbad8a\",\"incorrect_answers\":[\"IKEO\",\"IREA\",\"IKER\"],\"difficulty\":\"medium\"},{\"description\":\"What is the name of this company?\",\"name\":\"LEGO\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoQuiz%2Flego_logo.png?alt=media&amp;token=d255065b-355b-4e66-9a06-3eaf691e3589\",\"incorrect_answers\":[\"FEGA\",\"LEGA\",\"LEMA\"],\"difficulty\":\"medium\"},{\"description\":\"What is the name of this social app?\",\"name\":\"Tiktok\",\"img_url\":\"https://www.edigitalagency.com.au/wp-content/uploads/TikTok-icon-glyph.png\",\"incorrect_answers\":[\"Toktik\",\"Tiktik\",\"Toktok\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this company?\",\"name\":\"Netflix\",\"img_url\":\"https://1000logos.net/wp-content/uploads/2017/05/Netflix-Logo-2006.png\",\"incorrect_answers\":[\"Myflix\",\"Newflix\",\"Netflis\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this payments company?\",\"name\":\"Paypal\",\"img_url\":\"https://www.paypalobjects.com/webstatic/icon/pp258.png\",\"incorrect_answers\":[\"Palpay\",\"Payme\",\"Wepay\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this car company?\",\"name\":\"Tesla\",\"img_url\":\"https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Tesla_T_symbol.svg/800px-Tesla_T_symbol.svg.png\",\"incorrect_answers\":[\"Tata Motors\",\"Volvo\",\"Mercedes\"],\"difficulty\":\"medium\"},{\"description\":\"What is the name of this financial services company?\",\"name\":\"Visa\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoQuiz%2FVISA-logo.png?alt=media&amp;token=557f36f2-2f46-4a33-bc0d-df44974611f1\",\"incorrect_answers\":[\"Vita\",\"Vila\",\"Vis\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this car company?\",\"name\":\"Ferrari\",\"img_url\":\"https://www.escolacasa.com/wp-content/uploads/2021/03/Emblema_Ferrari.jpg\",\"incorrect_answers\":[\"Audi\",\"Bugatti\",\"Mercedes\"],\"difficulty\":\"medium\"},{\"description\":\"What is the name of this company?\",\"name\":\"Amazon\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoQuiz%2Famazon.webp?alt=media&amp;token=24912534-9aff-4967-b685-cab95c33f1be\",\"incorrect_answers\":[\"Alazon\",\"Amazob\",\"Abazos\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this company?\",\"name\":\"Nike\",\"img_url\":\"https://lh3.googleusercontent.com/FK8EcHV1SJGHeTUJCsUhCQl0hmQu-QbC4wG6bM59S0v-rLv-jQl16YC3LQ4x-ZpPwS1cUs_4Idap57kYgcTCOQFB\",\"incorrect_answers\":[\"Mike\",\"Mile\",\"Nile\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this company?\",\"name\":\"Adidas\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoQuiz%2Fadidas.png?alt=media&amp;token=6ab85092-f19f-43d7-b56b-49add8e74969\",\"incorrect_answers\":[\"Abibas\",\"Ardidas\",\"Amigas\"],\"difficulty\":\"easy\"},{\"description\":\"What is the name of this company?\",\"name\":\"Dell\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoQuiz%2Fdell.png?alt=media&amp;token=e080737a-446c-42a8-8b7a-5c65cb0b0453\",\"incorrect_answers\":[\"Bell\",\"Sell\",\"Dele\"],\"difficulty\":\"hard\"},{\"description\":\"What is the name of this company?\",\"name\":\"Levis\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoQuiz%2Flevis.png?alt=media&amp;token=6c878b7b-9096-47f2-aba9-1fe19896b4dd\",\"incorrect_answers\":[\"Legis\",\"Lebis\",\"Lives\"],\"difficulty\":\"hard\"},{\"description\":\"What is the name of this company?\",\"name\":\"Coca-Cola\",\"img_url\":\"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/LogoQuiz%2FCoca-Cola-Logo.png?alt=media&amp;token=1fdec8d8-71c8-4fbb-b548-6a3916959040\",\"incorrect_answers\":[\"Cola-Coca\",\"Cola-Cola\",\"Coca-Coca\"],\"difficulty\":\"hard\"}]</value>\n  </entry>\n  <entry>\n    <key>multichoice_quickquiz_difficulty</key>\n    <value>random</value>\n  </entry>\n  <entry>\n    <key>multichoice_skip_cost</key>\n    <value>1</value>\n  </entry>\n  <entry>\n    <key>math_quiz_operator_size</key>\n    <value>1</value>\n  </entry>\n  <entry>\n    <key>math_quiz_difficulty</key>\n    <value>easy</value>\n  </entry>\n</defaults>"
  },
  {
    "path": "core/remote-config/src/normal/kotlin/com/infinitepower/newquiz/core/remote_config/FirebaseRemoteConfigImpl.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config\n\nimport com.google.firebase.remoteconfig.FirebaseRemoteConfig\nimport com.google.firebase.remoteconfig.remoteConfigSettings\n\nclass FirebaseRemoteConfigImpl(\n    private val firebaseRemoteConfig: FirebaseRemoteConfig\n) : RemoteConfig {\n    override fun initialize(fetchInterval: Long) {\n        val remoteConfigSettings = remoteConfigSettings {\n            minimumFetchIntervalInSeconds = fetchInterval\n        }\n        firebaseRemoteConfig.setConfigSettingsAsync(remoteConfigSettings)\n        firebaseRemoteConfig.setDefaultsAsync(R.xml.remote_config_defaults)\n\n        firebaseRemoteConfig.fetchAndActivate()\n    }\n\n    override fun getString(key: String): String = firebaseRemoteConfig.getString(key)\n\n    override fun getLong(key: String): Long = firebaseRemoteConfig.getLong(key)\n\n    override fun getBoolean(key: String): Boolean = firebaseRemoteConfig.getBoolean(key)\n}"
  },
  {
    "path": "core/remote-config/src/normal/kotlin/com/infinitepower/newquiz/core/remote_config/initializer/RemoteConfigInitializer.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config.initializer\n\nimport android.content.Context\nimport android.util.Log\nimport androidx.startup.Initializer\nimport com.google.firebase.Firebase\nimport com.google.firebase.app\nimport com.google.firebase.remoteconfig.FirebaseRemoteConfig\nimport com.infinitepower.newquiz.core.initializer.CoreFirebaseInitializer\nimport com.infinitepower.newquiz.core.remote_config.FirebaseRemoteConfigImpl\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nclass RemoteConfigInitializer : Initializer<RemoteConfig> {\n    private companion object {\n        private const val TAG = \"RemoteConfigInitializer\"\n    }\n\n    @Provides\n    @Singleton\n    override fun create(@ApplicationContext context: Context): RemoteConfig {\n        val firebaseApp = Firebase.app\n        val firebaseRemoteConfig = FirebaseRemoteConfig.getInstance(firebaseApp)\n\n        Log.d(TAG, \"Initializing Firebase Remote Config...\")\n\n        val remoteConfig = FirebaseRemoteConfigImpl(firebaseRemoteConfig = firebaseRemoteConfig)\n        remoteConfig.initialize()\n\n        Log.d(TAG, \"Firebase Remote Config initialized successfully\")\n\n        return remoteConfig\n    }\n\n    override fun dependencies(): List<Class<out Initializer<*>>> {\n        return listOf(CoreFirebaseInitializer::class.java)\n    }\n}"
  },
  {
    "path": "core/remote-config/src/test/kotlin/com/infinitepower/newquiz/core/remote_config/LocalRemoteConfig.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config\n\n/**\n * A [RemoteConfig] implementation that uses a map of remote config values.\n * Only used for testing.\n */\ninternal class LocalRemoteConfig(\n    private val remoteConfigValues: Map<String, String>\n) : RemoteConfig {\n    override fun initialize(fetchInterval: Long) {\n        // Do nothing\n    }\n\n    override fun getString(key: String): String {\n        return remoteConfigValues[key] ?: throw NoValueForRemoteConfigKeyException(key)\n    }\n\n    override fun getLong(key: String): Long {\n        return remoteConfigValues[key]?.toLong() ?: throw NoValueForRemoteConfigKeyException(key)\n    }\n\n    override fun getBoolean(key: String): Boolean {\n        return remoteConfigValues[key]?.toBoolean() ?: throw NoValueForRemoteConfigKeyException(key)\n    }\n}\n"
  },
  {
    "path": "core/remote-config/src/test/kotlin/com/infinitepower/newquiz/core/remote_config/RemoteConfigTest.kt",
    "content": "package com.infinitepower.newquiz.core.remote_config\n\nimport com.google.common.truth.Truth.assertThat\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport org.junit.jupiter.api.assertThrows\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\ninternal class RemoteConfigTest {\n    private lateinit var remoteConfig: RemoteConfig\n\n    private val testValue1 = RemoteConfigValue<Int>(\"test_value_1\")\n\n    @Serializable\n    private data class TestDataClass(\n        val value1: Int,\n        val value2: String\n    )\n\n    private val testData2 = TestDataClass(1, \"test\")\n    private val testValue2 = RemoteConfigValue<TestDataClass>(\"test_value_2\")\n\n    private enum class TestEnum { A, B, C }\n    private val testData3 = TestEnum.A\n    private val testValue3 = RemoteConfigValue<TestEnum>(\"test_value_3\")\n\n    @Serializable\n    private enum class TestEnum2 {\n        @SerialName(\"a\") A,\n        @SerialName(\"b\") B,\n        @SerialName(\"c\") C\n    }\n    private val testData4 = TestEnum2.A\n    private val testValue4 = RemoteConfigValue<TestEnum2>(\"test_value_4\")\n\n    private data class TestDataClass2(val name: String)\n    private val testValue5 = RemoteConfigValue<TestDataClass2>(\"test_value_5\")\n\n\n    @BeforeTest\n    fun setUp() {\n        println(Json.encodeToString(testData4))\n\n        remoteConfig = LocalRemoteConfig(\n            remoteConfigValues = mapOf(\n                testValue1.key to \"1\",\n                testValue2.key to Json.encodeToString(testData2),\n                testValue3.key to testData3.name,\n                testValue4.key to Json.encodeToString(testData4),\n                testValue5.key to \"\"\"{ \"name\": \"test\" }\"\"\"\n            )\n        )\n        remoteConfig.initialize()\n    }\n\n    @Test\n    fun `test get value`() {\n        // Test for Int primitive type\n        val value = remoteConfig.get(testValue1)\n        assertThat(value).isEqualTo(1)\n\n        // Test for custom class with serialization\n        val value2 = remoteConfig.get(testValue2)\n        assertThat(value2).isEqualTo(testData2)\n\n        // When the enum class does not have serialization, it should be decoded using reflection\n        val value3 = remoteConfig.get(testValue3)\n        assertThat(value3).isEqualTo(testData3)\n\n        // When enum class has serialization, it should be decoded using the deserialization method\n        val value4 = remoteConfig.get(testValue4)\n        assertThat(value4).isEqualTo(testData4)\n\n        // When the class is not supported, an exception should be thrown\n        val exception = assertThrows<IllegalArgumentException> {\n            remoteConfig.get(testValue5)\n        }\n        assertThat(exception.message).isEqualTo(\"Unsupported type ${TestDataClass2::class}\")\n    }\n}\n"
  },
  {
    "path": "core/src/androidTest/AndroidManifest.xml",
    "content": "<manifest\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <application>\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            tools:node=\"remove\" />\n    </application>\n</manifest>"
  },
  {
    "path": "core/src/androidTest/java/com/infinitepower/newquiz/core/ui/components/RemainingTimeComponentTest.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components\n\nimport androidx.compose.ui.semantics.ProgressBarRangeInfo\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.assertRangeInfoEquals\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.infinitepower.newquiz.model.RemainingTime\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport kotlin.time.Duration.Companion.seconds\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\nclass RemainingTimeComponentTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun testInitialState() {\n        val maxTime = 30.seconds\n        val remainingTime = RemainingTime(30.seconds)\n\n        // Render the RemainingTimeComponent\n        composeTestRule.setContent {\n            RemainingTimeComponent(\n                remainingTime = remainingTime,\n                maxTime = maxTime\n            )\n        }\n\n        // Assert that the progress indicator is shown\n        composeTestRule\n            .onNodeWithTag(RemainingTimeComponentTestTags.PROGRESS_INDICATOR)\n            .assertIsDisplayed()\n            .assertRangeInfoEquals(\n                ProgressBarRangeInfo(\n                    current = 1f,\n                    range = 0f..1f\n                )\n            )\n\n        // Assert that the remaining time text is displayed without animation\n        composeTestRule\n            .onNodeWithText(\"30\")\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun testWarningState() {\n        val maxTime = 30.seconds\n        val remainingTime = RemainingTime(5.seconds)\n\n        // Render the RemainingTimeComponent\n        composeTestRule.setContent {\n            RemainingTimeComponent(\n                remainingTime = remainingTime,\n                maxTime = maxTime\n            )\n        }\n\n        // Assert that the progress indicator is shown\n        composeTestRule\n            .onNodeWithTag(RemainingTimeComponentTestTags.PROGRESS_INDICATOR)\n            .assertIsDisplayed()\n            .assertRangeInfoEquals(\n                ProgressBarRangeInfo(\n                    current = remainingTime.getRemainingPercent(maxTime).toFloat(),\n                    range = 0f..1f\n                )\n            )\n\n        // Assert that the remaining time text is displayed without animation\n        composeTestRule\n            .onNodeWithText(\"5\")\n            .assertIsDisplayed()\n    }\n}"
  },
  {
    "path": "core/src/androidTest/java/com/infinitepower/newquiz/core/ui/components/category/CategoryComponentTest.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.category\n\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertHasClickAction\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.assertIsEnabled\nimport androidx.compose.ui.test.assertIsNotEnabled\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithContentDescription\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class CategoryComponentTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun categoryComponent_titleAndImageDisplayed() {\n        val title = \"Test Title\"\n        val imageUrl = \"https://testimage.com/image.jpg\"\n\n        composeTestRule.setTestContent {\n            CategoryComponent(\n                modifier = Modifier.testTag(\"CategoryComponent\"),\n                title = title,\n                imageUrl = imageUrl,\n                enabled = true\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"CategoryComponent\")\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertHasClickAction()\n\n        composeTestRule\n            .onNodeWithContentDescription(\"Image category of $title\")\n            .assertIsDisplayed()\n\n        composeTestRule\n            .onNodeWithText(title)\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun categoryComponent_onClick() {\n        var clicked = false\n\n        val title = \"Test Title\"\n        val imageUrl = \"https://testimage.com/image.jpg\"\n\n        composeTestRule.setTestContent {\n            CategoryComponent(\n                modifier = Modifier.testTag(\"CategoryComponent\"),\n                title = title,\n                imageUrl = imageUrl,\n                enabled = true,\n                onClick = { clicked = true }\n            )\n        }\n        composeTestRule\n            .onNodeWithTag(\"CategoryComponent\")\n            .assertHasClickAction()\n            .performClick()\n\n        assert(clicked)\n    }\n\n    @Test\n    fun categoryComponent_disabled() {\n        val title = \"Test Title\"\n        val imageUrl = \"https://testimage.com/image.jpg\"\n\n        composeTestRule.setTestContent {\n            CategoryComponent(\n                modifier = Modifier.testTag(\"CategoryComponent\"),\n                title = title,\n                imageUrl = imageUrl,\n                enabled = false\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"CategoryComponent\")\n            .assertIsDisplayed()\n            .assertIsNotEnabled()\n    }\n}"
  },
  {
    "path": "core/src/androidTest/java/com/infinitepower/newquiz/core/ui/components/category/CategoryConnectionInfoBadgeTest.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.category\n\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithContentDescription\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport org.junit.Rule\nimport org.junit.runner.RunWith\nimport kotlin.test.Test\n\n/**\n * Tests for [CategoryConnectionInfoBadge] component.\n */\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class CategoryConnectionInfoBadgeTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun test_requireConnection_and_noShowTextByDefault() {\n        composeTestRule.setTestContent {\n            CategoryConnectionInfoBadge(\n                modifier = Modifier.testTag(COMPONENT_TAG),\n                requireConnection = true,\n                showTextByDefault = false\n            )\n        }\n\n        composeTestRule\n            .onNodeWithText(\"Requires internet connection\")\n            .assertDoesNotExist()\n\n        composeTestRule\n            .onNodeWithContentDescription(\"Requires internet connection\")\n            .assertIsDisplayed()\n            .performClick() // Expand the badge to show the text\n\n        composeTestRule\n            .onNodeWithText(\"Requires internet connection\")\n            .assertIsDisplayed()\n            .performClick() // Collapse the badge to hide the text\n            .assertDoesNotExist()\n\n        composeTestRule\n            .onNodeWithContentDescription(\"Requires internet connection\")\n            .assertIsDisplayed()\n    }\n\n    @Test\n    fun test_requireConnection_and_showTextByDefault() {\n        composeTestRule.setTestContent {\n            CategoryConnectionInfoBadge(\n                modifier = Modifier.testTag(COMPONENT_TAG),\n                requireConnection = true,\n                showTextByDefault = true\n            )\n        }\n\n        composeTestRule\n            .onNodeWithContentDescription(\"Requires internet connection\")\n            .assertIsDisplayed()\n\n        composeTestRule\n            .onNodeWithText(\"Requires internet connection\")\n            .assertIsDisplayed()\n            .performClick() // Collapse the badge to hide the text\n            .assertDoesNotExist()\n\n        composeTestRule\n            .onNodeWithContentDescription(\"Requires internet connection\")\n            .assertIsDisplayed()\n    }\n\n    companion object {\n        private const val COMPONENT_TAG = \"offline_category_badge\"\n    }\n}"
  },
  {
    "path": "core/src/androidTest/java/com/infinitepower/newquiz/core/ui/components/icon/button/BackIconButtonTest.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.icon.button\n\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertHasClickAction\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.performClick\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.infinitepower.newquiz.core.testing.clearExistingImages\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport org.junit.BeforeClass\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class BackIconButtonTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    companion object {\n        private const val BUTTON_GOLDEN_NAME = \"back_button\"\n\n        @JvmStatic\n        @BeforeClass\n        fun clearExistingImagesBeforeStart() {\n            clearExistingImages(BUTTON_GOLDEN_NAME)\n        }\n    }\n\n    @Test\n    fun backIconButton_shouldRenderCorrectly() {\n        var clicked = false\n\n        composeTestRule.setTestContent {\n            BackIconButton(\n                onClick = { clicked = true },\n                modifier = Modifier.testTag(\"BackButton\")\n            )\n        }\n\n        // Assert that an IconButton and an Icon are rendered with the correct attributes\n        composeTestRule\n            .onNodeWithTag(\"BackButton\")\n            .assertExists()\n            .assertIsDisplayed()\n            .assertHasClickAction()\n            .performClick()\n\n        assert(clicked)\n    }\n\n    /*\n    @Test\n    fun test_backIconButton_screenshot() {\n        composeTestRule.setContent {\n            Surface {\n                BackIconButton(\n                    onClick = {},\n                    modifier = Modifier.testTag(\"BackButton\")\n                )\n            }\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"BackButton\")\n            .assertMatchesGolden(BUTTON_GOLDEN_NAME, \"buttons\")\n    }\n\n     */\n}\n"
  },
  {
    "path": "core/src/androidTest/java/com/infinitepower/newquiz/core/ui/components/skip_question/SkipQuestionDialogTest.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.skip_question\n\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Tests for [SkipQuestionDialog].\n */\n@SmallTest\n@RunWith(AndroidJUnit4::class)\n@OptIn(ExperimentalMaterial3Api::class)\ninternal class SkipQuestionDialogTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun skipQuestionDialog_CanSkip_ShowAlertDialog() {\n        val userDiamonds = 10\n        val skipCost = 5\n        var skipClickCalled = false\n        var dismissClickCalled = false\n\n        composeTestRule.setTestContent {\n            SkipQuestionDialog(\n                userDiamonds = userDiamonds,\n                skipCost = skipCost,\n                onSkipClick = { skipClickCalled = true },\n                onDismissClick = { dismissClickCalled = true }\n            )\n        }\n\n        assertThat(skipClickCalled).isFalse()\n        assertThat(dismissClickCalled).isFalse()\n\n        composeTestRule.onNodeWithText(\"Skip question?\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"You have $userDiamonds diamonds, do you want to use $skipCost diamonds to skip this question?\").assertIsDisplayed()\n\n        composeTestRule.onNodeWithText(\"Close\").assertIsDisplayed()\n        composeTestRule\n            .onNodeWithText(\"Skip\")\n            .assertIsDisplayed()\n            .performClick()\n\n\n        assertThat(skipClickCalled).isTrue()\n        assertThat(dismissClickCalled).isTrue()\n\n        skipClickCalled = false\n        dismissClickCalled = false\n\n        composeTestRule.onNodeWithText(\"Close\").performClick()\n        assertThat(dismissClickCalled).isTrue()\n        assertThat(skipClickCalled).isFalse()\n    }\n\n    @Test\n    fun skipQuestionDialog_CannotSkip_ShowNoDiamondsDialog() {\n        val userDiamonds = 3\n        val skipCost = 5\n        var skipClickCalled = false\n        var dismissClickCalled = false\n\n        composeTestRule.setTestContent {\n            SkipQuestionDialog(\n                userDiamonds = userDiamonds,\n                skipCost = skipCost,\n                onSkipClick = { skipClickCalled = true },\n                onDismissClick = { dismissClickCalled = true }\n            )\n        }\n\n        composeTestRule.onNodeWithText(\"Skip question?\").assertDoesNotExist()\n        composeTestRule.onNodeWithText(\"No diamonds\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"You don't have diamonds to skip this question!\").assertIsDisplayed()\n\n        composeTestRule.onNodeWithText(\"Skip\").assertDoesNotExist()\n        composeTestRule\n            .onNodeWithText(\"Close\")\n            .assertIsDisplayed()\n            .performClick()\n\n        assertThat(dismissClickCalled).isTrue()\n        assertThat(skipClickCalled).isFalse()\n    }\n}"
  },
  {
    "path": "core/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n</manifest>"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/NumberFormatter.kt",
    "content": "package com.infinitepower.newquiz.core\n\nimport android.icu.util.LocaleData\nimport android.icu.util.ULocale\nimport android.os.Build\nimport androidx.core.text.util.LocalePreferences\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.regional_preferences.DistanceUnitType\nimport com.infinitepower.newquiz.model.regional_preferences.RegionalPreferences\nimport com.infinitepower.newquiz.model.regional_preferences.TemperatureUnit\nimport java.math.RoundingMode\nimport java.text.DateFormat\nimport java.text.NumberFormat\nimport java.util.Locale\nimport kotlin.math.pow\n\nsealed class NumberFormatter(\n    val formatType: NumberFormatType\n) {\n    abstract fun formatValueToString(\n        value: Number,\n        helperValueSuffix: String? = null,\n        regionalPreferences: RegionalPreferences = RegionalPreferences(),\n    ): String\n\n    companion object {\n        fun from(formatType: NumberFormatType): NumberFormatter {\n            return when (formatType) {\n                NumberFormatType.DEFAULT -> Default\n                NumberFormatType.DATE -> Date\n                NumberFormatType.TIME -> Time\n                NumberFormatType.DATETIME -> DateTime\n                NumberFormatType.PERCENTAGE -> Percentage\n                NumberFormatType.TEMPERATURE -> Temperature\n                NumberFormatType.DISTANCE -> Distance\n            }\n        }\n\n        /**\n         * Formats the [value] to a string with the [helperValueSuffix] if it's not null.\n         */\n        private fun valueWithSuffix(\n            value: String,\n            helperValueSuffix: String? = null\n        ): String = if (helperValueSuffix != null) \"$value $helperValueSuffix\" else value\n\n        private fun formatNumberToString(\n            value: Number,\n            helperValueSuffix: String?,\n            locale: Locale\n        ): String {\n            val numberFormat = NumberFormat.getNumberInstance(locale)\n            val numberFormatted = numberFormat.format(value)\n\n            return valueWithSuffix(numberFormatted, helperValueSuffix)\n        }\n    }\n\n    override fun toString(): String = formatType.name.lowercase()\n\n    /**\n     * The format type of the quiz is a number.\n     */\n    object Default : NumberFormatter(formatType = NumberFormatType.DEFAULT) {\n        /**\n         * Formats the double (number) [value] to a string with the [helperValueSuffix] if it's not null.\n         */\n        override fun formatValueToString(\n            value: Number,\n            helperValueSuffix: String?,\n            regionalPreferences: RegionalPreferences\n        ): String = formatNumberToString(\n            value = value,\n            helperValueSuffix = helperValueSuffix,\n            locale = regionalPreferences.locale\n        )\n    }\n\n    object Date : NumberFormatter(formatType = NumberFormatType.DATE) {\n        /**\n         * Formats the [value] to a string with the [helperValueSuffix] if it's not null.\n         * The [value] is a timestamp in milliseconds.\n         */\n        override fun formatValueToString(\n            value: Number,\n            helperValueSuffix: String?,\n            regionalPreferences: RegionalPreferences\n        ): String {\n            val dateFormat =\n                DateFormat.getDateInstance(DateFormat.MEDIUM, regionalPreferences.locale)\n            val dateFormatted = dateFormat.format(value.toLong())\n\n            return valueWithSuffix(dateFormatted, helperValueSuffix)\n        }\n    }\n\n    object Time : NumberFormatter(formatType = NumberFormatType.TIME) {\n        /**\n         * Formats the [value] to a string with the [helperValueSuffix] if it's not null.\n         * The [value] is a timestamp in milliseconds.\n         */\n        override fun formatValueToString(\n            value: Number,\n            helperValueSuffix: String?,\n            regionalPreferences: RegionalPreferences\n        ): String {\n            val timeFormat =\n                DateFormat.getTimeInstance(DateFormat.SHORT, regionalPreferences.locale)\n            val timeFormatted = timeFormat.format(value.toLong())\n\n            return valueWithSuffix(timeFormatted, helperValueSuffix)\n        }\n    }\n\n    object DateTime : NumberFormatter(formatType = NumberFormatType.DATETIME) {\n        /**\n         * Formats the [value] to a string with the [helperValueSuffix] if it's not null.\n         * The [value] is a timestamp in milliseconds.\n         */\n        override fun formatValueToString(\n            value: Number,\n            helperValueSuffix: String?,\n            regionalPreferences: RegionalPreferences\n        ): String {\n            val dateTimeFormat =\n                DateFormat.getDateTimeInstance(\n                    DateFormat.MEDIUM,\n                    DateFormat.SHORT,\n                    regionalPreferences.locale\n                )\n            val dateTimeFormatted = dateTimeFormat.format(value.toLong())\n\n            return valueWithSuffix(dateTimeFormatted, helperValueSuffix)\n        }\n    }\n\n    object Percentage : NumberFormatter(\n        formatType = NumberFormatType.PERCENTAGE\n    ) {\n        /**\n         * Formats the [value] to a string with the [helperValueSuffix] if it's not null.\n         * The [value] is a percentage in double.\n         */\n        override fun formatValueToString(\n            value: Number,\n            helperValueSuffix: String?,\n            regionalPreferences: RegionalPreferences\n        ): String {\n            val numberFormat = NumberFormat.getPercentInstance(regionalPreferences.locale)\n            val percentageFormatted = numberFormat.format(value.toDouble())\n\n            return valueWithSuffix(percentageFormatted, helperValueSuffix)\n        }\n    }\n\n    object Temperature : NumberFormatter(\n        formatType = NumberFormatType.TEMPERATURE\n    ) {\n        /**\n         * Formats the [value] to a string, the [helperValueSuffix] is the [LocalePreferences.TemperatureUnit] from the [regionalPreferences].\n         *\n         * @param value The value to format.\n         * @param helperValueSuffix The [LocalePreferences.TemperatureUnit] key.\n         */\n        override fun formatValueToString(\n            value: Number,\n            helperValueSuffix: String?,\n            regionalPreferences: RegionalPreferences,\n        ): String {\n            checkNotNull(helperValueSuffix) { \"The helperValueSuffix cannot be null for the temperature format type.\" }\n            val valueTemperatureUnit = TemperatureUnit.fromKey(helperValueSuffix)\n\n            // If the user has configured a temperature unit, use it instead of the locale\n            val convertTemperatureUnitKey = regionalPreferences.temperatureUnit?.key\n                ?: LocalePreferences.getTemperatureUnit(regionalPreferences.locale)\n\n            // If the value temperature unit is the same as the convert temperature unit, it's not necessary to convert\n            // the value, just return the value with the suffix\n            if (valueTemperatureUnit.key == convertTemperatureUnitKey) {\n                return valueWithSuffix(value.toString(), valueTemperatureUnit.value)\n            }\n\n            val convertTemperatureUnit = regionalPreferences.temperatureUnit\n                ?: TemperatureUnit.fromKey(convertTemperatureUnitKey)\n\n            val convertedValue = valueTemperatureUnit.convert(\n                to = convertTemperatureUnit,\n                value = value.toDouble()\n            )\n\n            if (convertedValue.isNaN()) {\n                return valueWithSuffix(\"NaN\", convertTemperatureUnit.value)\n            }\n\n            val valueStr = convertedValue.toBigDecimal()\n                .setScale(2, RoundingMode.HALF_EVEN)\n                .stripTrailingZeros()\n                .toPlainString()\n\n            return valueWithSuffix(valueStr, convertTemperatureUnit.value)\n        }\n    }\n\n    object Distance : NumberFormatter(formatType = NumberFormatType.DISTANCE) {\n        enum class DistanceUnit(\n            val key: String,\n            val value: String,\n            val type: DistanceUnitType,\n        ) {\n            METER(\n                key = \"meter\",\n                value = \"m\",\n                type = DistanceUnitType.METRIC\n            ),\n            KILOMETER(\n                key = \"kilometer\",\n                value = \"km\",\n                type = DistanceUnitType.METRIC\n            ),\n            SQUARE_KILOMETER(\n                key = \"square_kilometer\",\n                value = \"km²\",\n                type = DistanceUnitType.METRIC\n            ),\n            FOOT(\n                key = \"foot\",\n                value = \"ft\",\n                type = DistanceUnitType.IMPERIAL\n            ),\n            MILE(\n                key = \"mile\",\n                value = \"mi\",\n                type = DistanceUnitType.IMPERIAL\n            ),\n            SQUARE_MILE(\n                key = \"square_mile\",\n                value = \"mi²\",\n                type = DistanceUnitType.IMPERIAL\n            );\n\n            companion object {\n                fun fromKey(key: String): DistanceUnit = entries\n                    .firstOrNull { it.key == key || it.value == key }\n                    ?: throw IllegalArgumentException(\"Unknown distance unit: $key\")\n\n                private const val FOOT_TO_METER_MULTIPLIER = 0.3048\n                private const val MILE_TO_KILOMETER_MULTIPLIER = 1.609344\n                private const val METER_TO_FOOT_MULTIPLIER = 3.2808399\n                private const val KILOMETER_TO_MILE_MULTIPLIER = 0.621371192\n\n                fun convert(\n                    value: Double,\n                    from: DistanceUnit,\n                    to: DistanceUnitType\n                ): Pair<Double, DistanceUnit> {\n                    if (from.type == to) return Pair(value, from)\n\n                    return when (to) {\n                        // Convert to metric\n                        DistanceUnitType.METRIC -> {\n                            when (from) {\n                                FOOT -> Pair(value * FOOT_TO_METER_MULTIPLIER, METER)\n                                MILE -> Pair(value * MILE_TO_KILOMETER_MULTIPLIER, KILOMETER)\n                                SQUARE_MILE -> Pair(\n                                    value * MILE_TO_KILOMETER_MULTIPLIER.pow(2),\n                                    SQUARE_KILOMETER\n                                )\n\n                                METER, KILOMETER, SQUARE_KILOMETER -> Pair(\n                                    value,\n                                    from\n                                ) // Already metric\n                            }\n                        }\n\n                        // Convert to imperial\n                        DistanceUnitType.IMPERIAL -> {\n                            when (from) {\n                                METER -> Pair(value * METER_TO_FOOT_MULTIPLIER, FOOT)\n                                KILOMETER -> Pair(value * KILOMETER_TO_MILE_MULTIPLIER, MILE)\n                                SQUARE_KILOMETER -> Pair(\n                                    value * KILOMETER_TO_MILE_MULTIPLIER.pow(2),\n                                    SQUARE_MILE\n                                )\n\n                                FOOT, MILE, SQUARE_MILE -> Pair(value, from) // Already imperial\n                            }\n                        }\n                    }\n                }\n            }\n\n            override fun toString(): String = key\n        }\n\n        private fun Locale.getDistanceUnitType(): DistanceUnitType {\n            return if (isMetric()) {\n                DistanceUnitType.METRIC\n            } else {\n                DistanceUnitType.IMPERIAL\n            }\n        }\n\n        private fun Locale.isMetric(): Boolean {\n            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                val uLocale = ULocale.forLocale(this)\n                val measurementSystem = LocaleData.getMeasurementSystem(uLocale)\n                measurementSystem == LocaleData.MeasurementSystem.SI\n            } else {\n                isMetricLower()\n            }\n        }\n\n        private fun Locale.isMetricLower(): Boolean {\n            return when (country.uppercase(this)) {\n                \"US\", \"LR\", \"MM\" -> false\n                else -> true\n            }\n        }\n\n        override fun formatValueToString(\n            value: Number,\n            helperValueSuffix: String?,\n            regionalPreferences: RegionalPreferences\n        ): String {\n            checkNotNull(helperValueSuffix) { \"The helperValueSuffix cannot be null for the distance format type.\" }\n            val valueDistanceUnit = DistanceUnit.fromKey(helperValueSuffix)\n\n            // If the user has configured a distance unit, use it instead of the locale\n            val convertDistanceUnitType =\n                regionalPreferences.distanceUnitType\n                    ?: regionalPreferences.locale.getDistanceUnitType()\n\n            // If the value distance unit type is the same as the user distance unit type, return the value with the suffix\n            if (valueDistanceUnit.type == convertDistanceUnitType) {\n                return formatNumberToString(\n                    value = value,\n                    helperValueSuffix = valueDistanceUnit.value,\n                    locale = regionalPreferences.locale\n                )\n            }\n\n            val (convertedValue, convertedUnit) = DistanceUnit.convert(\n                from = valueDistanceUnit,\n                to = convertDistanceUnitType,\n                value = value.toDouble()\n            )\n\n            if (convertedValue.isNaN()) {\n                return valueWithSuffix(\"NaN\", convertedUnit.value)\n            }\n\n            val valueRounded = convertedValue.toBigDecimal()\n                .setScale(2, RoundingMode.HALF_EVEN)\n                .stripTrailingZeros()\n                .toDouble()\n\n            return formatNumberToString(\n                // Remove the decimals from the converted value\n                value = valueRounded,\n                helperValueSuffix = convertedUnit.value,\n                locale = regionalPreferences.locale\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/common/BaseApiUrls.kt",
    "content": "package com.infinitepower.newquiz.core.common\n\nobject BaseApiUrls {\n    const val NEWQUIZ = \"https://newquiz-app.vercel.app\"\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/common/Common.kt",
    "content": "package com.infinitepower.newquiz.core.common\n\nconst val DEFAULT_USER_PHOTO = \"https://firebasestorage.googleapis.com/v0/b/newsocial-app.appspot.com/o/Default%2Fuser_default_image.png?alt=media&token=69389847-7158-42aa-aab5-fc6a5499d3c7\""
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/common/compose/preview/BooleanPreviewParameterProvider.kt",
    "content": "package com.infinitepower.newquiz.core.common.compose.preview\n\nimport androidx.compose.ui.tooling.preview.PreviewParameterProvider\n\nclass BooleanPreviewParameterProvider : PreviewParameterProvider<Boolean> {\n    override val values: Sequence<Boolean>\n        get() = sequenceOf(true, false)\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/common/database/DatabaseCommon.kt",
    "content": "package com.infinitepower.newquiz.core.common.database\n\nobject DatabaseCommon {\n    object UserCollection {\n        const val NAME = \"users\"\n\n        const val FIELD_DIAMONDS = \"data.diamonds\"\n        const val FIELD_TOTAL_XP = \"data.totalXp\"\n        const val FIELD_LAST_QUIZ_TIMES = \"data.multiChoiceQuizData.lastQuizTimes\"\n        const val FIELD_TOTAL_QUESTIONS_PLAYED = \"data.multiChoiceQuizData.totalQuestionsPlayed\"\n        const val FIELD_CORRECT_ANSWERS = \"data.multiChoiceQuizData.totalCorrectAnswers\"\n\n        const val FIELD_WORDLE_WORDS_PLAYED = \"data.wordleData.wordsPlayed\"\n        const val FIELD_WORDLE_WORDS_CORRECT = \"data.wordleData.wordsCorrect\"\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/compose/preferences/LocalPreferenceEnabledStatus.kt",
    "content": "package com.infinitepower.newquiz.core.compose.preferences\n\nimport androidx.compose.runtime.ProvidableCompositionLocal\nimport androidx.compose.runtime.compositionLocalOf\nimport androidx.compose.runtime.structuralEqualityPolicy\n\nval LocalPreferenceEnabledStatus: ProvidableCompositionLocal<Boolean> =\n    compositionLocalOf(structuralEqualityPolicy()) { true }"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/di/KtorModule.kt",
    "content": "package com.infinitepower.newquiz.core.di\n\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\nimport io.ktor.client.HttpClient\nimport io.ktor.client.engine.okhttp.OkHttp\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject KtorModule {\n    @Provides\n    @Singleton\n    fun providerKtorClient(): HttpClient = HttpClient(OkHttp)\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/di/NetworkStatusModule.kt",
    "content": "package com.infinitepower.newquiz.core.di\n\nimport com.infinitepower.newquiz.core.network.NetworkStatusTracker\nimport com.infinitepower.newquiz.core.network.NetworkStatusTrackerImpl\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\n\n@Module\n@InstallIn(SingletonComponent::class)\nabstract class NetworkStatusModule {\n    @Binds\n    abstract fun bindNetworkStatusTracker(\n        networkStatusTrackerImpl: NetworkStatusTrackerImpl\n    ): NetworkStatusTracker\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/game/ComparisonQuizCore.kt",
    "content": "package com.infinitepower.newquiz.core.game\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizHelperValueState\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\n\n/**\n * Represents the core functionality of a comparison quiz.\n */\ninterface ComparisonQuizCore :\n    GameCore<ComparisonQuizCore.QuizData, ComparisonQuizCore.InitializationData>, SkipGame {\n    /**\n     * Handles the click event on an answer in the comparison quiz.\n     *\n     * @param answer The selected answer.\n     */\n    fun onAnswerClicked(answer: ComparisonQuizItem)\n\n    /**\n     * Represents the data structure for a comparison quiz used in the [ComparisonQuizCore].\n     *\n     * @property questions The list of comparison quiz items.\n     * @property questionDescription The description of the quiz question.\n     * @property currentQuestion The current question in the quiz.\n     * @property comparisonMode The comparison mode for the quiz.\n     * @property currentPosition The current position in the quiz.\n     * @property isGameOver A flag indicating if the game is over.\n     * @property firstItemHelperValueState The state of the first item helper value.\n     * @property skippedAnswers The number of skipped answers.\n     */\n    @Keep\n    data class QuizData(\n        val category: ComparisonQuizCategory? = null,\n        val questions: List<ComparisonQuizItem> = emptyList(),\n        val questionDescription: String? = null,\n        val currentQuestion: ComparisonQuizQuestion? = null,\n        val comparisonMode: ComparisonMode = ComparisonMode.GREATER,\n        val currentPosition: Int = 0,\n        val isGameOver: Boolean = false,\n        val isLastQuestionCorrect: Boolean = false,\n        val firstItemHelperValueState: ComparisonQuizHelperValueState = ComparisonQuizHelperValueState.HIDDEN,\n        val skippedAnswers: Int = 0\n    ) {\n        /**\n         * Returns the next question and updates the current question.\n         *\n         * If the current question is null, it means it is a new game and the function will\n         * retrieve the first two questions from the questions list and remove them from the list.\n         *\n         * If the current question is not null, it means it is not a new game and the function will\n         * retrieve the first question from the questions list and remove it from the list.\n         *\n         * @return The next question.\n         * @throws GameOverException If the questions list is empty.\n         * @throws GameOverException If the questions list has less than two items.\n         */\n        fun getNextQuestion(skipped: Boolean = false): QuizData {\n            requireNotNull(category) { \"Category cannot be null\" }\n            if (questions.isEmpty()) {\n                throw GameOverException(\"Questions list is empty\")\n            }\n\n            val newQuestions = questions.toMutableList()\n\n            // Checks if is new game, if so, gets the two first questions\n            // And then removes from the questions list\n            val newCurrentQuestion = if (currentQuestion == null) {\n                // Check if there is at least two questions\n                if (newQuestions.size < 2) {\n                    throw GameOverException(\"Questions list has less than two items\")\n                }\n\n                val firstQuestion = newQuestions.first()\n                val secondQuestion = newQuestions[1]\n\n                // Remove the two questions\n                newQuestions.removeAt(0)\n                newQuestions.removeAt(0)\n\n                ComparisonQuizQuestion(\n                    questions = firstQuestion to secondQuestion,\n                    categoryId = category.id,\n                    comparisonMode = comparisonMode\n                )\n            } else {\n                // If is not a new game, gets the first question from the questions list\n                // And then removes from the questions list\n                val firstQuestion = newQuestions.first()\n                newQuestions.removeAt(0)\n\n                currentQuestion.nextQuestion(firstQuestion)\n            }\n\n            val isNewGame = currentQuestion == null\n\n            return copy(\n                questions = newQuestions,\n                currentQuestion = newCurrentQuestion,\n                currentPosition = currentPosition + 1,\n                // If it is a new game, then it should show the current helper value\n                // If it is next question, then it should show the helper value\n                firstItemHelperValueState = if (isNewGame) {\n                    firstItemHelperValueState\n                } else {\n                    ComparisonQuizHelperValueState.NORMAL\n                },\n                // If the question was skipped, then it should increment the skipped questions\n                skippedAnswers = if (skipped) {\n                    this.skippedAnswers + 1\n                } else {\n                    this.skippedAnswers\n                }\n            )\n        }\n    }\n\n    /**\n     * Represents the initial data for the [ComparisonQuizCore].\n     *\n     * @property category The category of the comparison quiz.\n     * @property comparisonMode The comparison mode for the quiz.\n     */\n    @Keep\n    data class InitializationData(\n        val category: ComparisonQuizCategory,\n        val comparisonMode: ComparisonMode,\n        val initialItems: List<ComparisonQuizItemEntity> = emptyList()\n    )\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/game/GameCore.kt",
    "content": "package com.infinitepower.newquiz.core.game\n\nimport kotlinx.coroutines.flow.StateFlow\n\nclass GameOverException(\n    override val message: String\n) : Exception(message)\n\n/**\n * Represents the core functionality of a game, providing the basic structure and operations.\n *\n * @param QuizData The data type representing the quiz-specific information.\n * @param InitializationData The data type representing the initialization parameters for the game.\n */\nsealed interface GameCore<QuizData, InitializationData> {\n    /**\n     * A [StateFlow] that emits the current quiz data.\n     */\n    val quizDataFlow: StateFlow<QuizData>\n\n    /**\n     * Initializes and starts the game with the given initial data.\n     * This method should be called only once.\n     *\n     * @param initializationData The initial data required to set up the game.\n     *\n     */\n    suspend fun initializeGame(initializationData: InitializationData)\n\n    /**\n     * Starts the game.\n     */\n    fun startGame()\n\n    /**\n     * Ends the game.\n     */\n    fun endGame()\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/game/SkipGame.kt",
    "content": "package com.infinitepower.newquiz.core.game\n\ninterface SkipGame {\n    val skipCost: UInt\n\n    suspend fun getUserDiamonds(): UInt\n\n    suspend fun skip()\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/math/evaluator/Expressions.kt",
    "content": "package com.infinitepower.newquiz.core.math.evaluator\n\nimport com.infinitepower.newquiz.core.math.evaluator.internal.Function\nimport com.infinitepower.newquiz.core.math.evaluator.internal.Evaluator\nimport com.infinitepower.newquiz.core.math.evaluator.internal.Expr\nimport com.infinitepower.newquiz.core.math.evaluator.internal.Parser\nimport com.infinitepower.newquiz.core.math.evaluator.internal.Scanner\nimport com.infinitepower.newquiz.core.math.evaluator.internal.Token\nimport java.math.BigDecimal\nimport java.math.MathContext\nimport java.math.RoundingMode\nimport kotlin.math.log\nimport kotlin.math.log10\nimport kotlin.math.sqrt\n\nclass ExpressionException(message: String) : RuntimeException(message)\n\nclass Expressions {\n    private val evaluator = Evaluator()\n\n    init {\n        define(\"π\", Math.PI)\n        define(\"e\", Math.E)\n\n        evaluator.addFunction(\"ln\", object : Function() {\n            override fun call(arguments: List<BigDecimal>): BigDecimal {\n                if (arguments.size != 1)\n                    throw ExpressionException(\"ln requires one argument\")\n\n                return log(arguments.first().toDouble(), Math.E).toBigDecimal()\n            }\n        })\n\n        evaluator.addFunction(\"log\", object : Function() {\n            override fun call(arguments: List<BigDecimal>): BigDecimal {\n                if (arguments.size != 1)\n                    throw ExpressionException(\"log requires one argument\")\n\n                return log10(arguments.first().toDouble()).toBigDecimal()\n            }\n        })\n\n        evaluator.addFunction(\"√\", object : Function() {\n            override fun call(arguments: List<BigDecimal>): BigDecimal {\n                if (arguments.size != 1)\n                    throw ExpressionException(\"square root requires one argument\")\n\n                return sqrt(arguments.first().toDouble()).toBigDecimal()\n            }\n        })\n    }\n\n    val precision: Int\n        get() = evaluator.mathContext.precision\n\n    val roundingMode: RoundingMode\n        get() = evaluator.mathContext.roundingMode\n\n    fun setPrecision(precision: Int): Expressions {\n        evaluator.mathContext = MathContext(precision, roundingMode)\n\n\n        return this\n    }\n\n    fun setRoundingMode(roundingMode: RoundingMode): Expressions {\n        evaluator.mathContext = MathContext(precision, roundingMode)\n\n        return this\n    }\n\n    fun define(name: String, value: Long): Expressions {\n        define(name, value.toString())\n\n        return this\n    }\n\n    fun define(name: String, value: Double): Expressions {\n        define(name, value.toString())\n\n        return this\n    }\n\n    fun define(name: String, value: BigDecimal): Expressions {\n        define(name, value.toPlainString())\n\n        return this\n    }\n\n    fun define(name: String, expression: String): Expressions {\n        val expr = parse(expression)\n        evaluator.define(name, expr)\n\n        return this\n    }\n\n    fun addFunction(name: String, function: Function): Expressions {\n        evaluator.addFunction(name, function)\n\n        return this\n    }\n\n    fun addFunction(name: String, func: (List<BigDecimal>) -> BigDecimal): Expressions {\n        evaluator.addFunction(name, object : Function() {\n            override fun call(arguments: List<BigDecimal>): BigDecimal {\n                return func(arguments)\n            }\n        })\n\n        return this\n    }\n\n    fun eval(expression: String): BigDecimal {\n        return evaluator.eval(parse(expression))\n    }\n\n    /**\n     * eval an expression then round it with {@link Evaluator#mathContext} and call toEngineeringString <br>\n     * if error will return message from Throwable\n     * @param expression String\n     * @return String\n     */\n    fun evalToString(expression: String): String {\n        return try {\n            evaluator\n                .eval(parse(expression))\n                .round(evaluator.mathContext)\n                .stripTrailingZeros()\n                .toEngineeringString()\n        } catch (e: Throwable) {\n            e.cause?.message ?: e.message ?: \"unknown error\"\n        }\n    }\n\n    private fun parse(expression: String): Expr {\n        return parse(scan(expression))\n    }\n\n    private fun parse(tokens: List<Token>): Expr {\n        return Parser(tokens).parse()\n    }\n\n    private fun scan(expression: String): List<Token> {\n        return Scanner(expression, evaluator.mathContext).scanTokens()\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/math/evaluator/internal/Evaluator.kt",
    "content": "package com.infinitepower.newquiz.core.math.evaluator.internal\n\nimport com.infinitepower.newquiz.core.math.evaluator.ExpressionException\nimport java.math.BigDecimal\nimport java.math.MathContext\nimport com.infinitepower.newquiz.core.math.evaluator.internal.TokenType.*\nimport java.math.RoundingMode\nimport kotlin.math.pow\n\ninternal class Evaluator : ExprVisitor<BigDecimal> {\n    internal var mathContext: MathContext = MathContext.DECIMAL64\n\n    private val variables: LinkedHashMap<String, BigDecimal> = linkedMapOf()\n    private val functions: MutableMap<String, Function> = mutableMapOf()\n\n    private fun define(name: String, value: BigDecimal) {\n        variables += name to value\n    }\n\n    fun define(name: String, expr: Expr): Evaluator {\n        define(name.lowercase(), eval(expr))\n        return this\n    }\n\n    fun addFunction(name: String, function: Function): Evaluator {\n        functions += name.lowercase() to function\n        return this\n    }\n\n    fun eval(expr: Expr): BigDecimal {\n        return expr.accept(this)\n    }\n\n    override fun visitAssignExpr(expr: AssignExpr): BigDecimal {\n        val value = eval(expr.value)\n\n        define(expr.name.lexeme, value)\n\n        return value\n    }\n\n    override fun visitLogicalExpr(expr: LogicalExpr): BigDecimal {\n        val left = expr.left\n        val right = expr.right\n\n        return when (expr.operator.type) {\n            BAR_BAR -> left or right\n            AMP_AMP -> left and right\n            else -> throw ExpressionException(\n                \"Invalid logical operator '${expr.operator.lexeme}'\"\n            )\n        }\n    }\n\n    override fun visitBinaryExpr(expr: BinaryExpr): BigDecimal {\n        val left = eval(expr.left)\n        val right = eval(expr.right)\n\n        return when (expr.operator.type) {\n            PLUS -> left + right\n            MINUS -> left - right\n            STAR -> left * right\n            SLASH -> left.divide(right, mathContext)\n            MODULO -> left.remainder(right, mathContext)\n            EXPONENT -> left pow right\n            EQUAL_EQUAL -> (left == right).toBigDecimal()\n            NOT_EQUAL -> (left != right).toBigDecimal()\n            GREATER -> (left > right).toBigDecimal()\n            GREATER_EQUAL -> (left >= right).toBigDecimal()\n            LESS -> (left < right).toBigDecimal()\n            LESS_EQUAL -> (left <= right).toBigDecimal()\n            else -> throw ExpressionException(\"Invalid binary operator '${expr.operator.lexeme}'\")\n        }\n    }\n\n    private fun Int.factorial() = (2..this).fold(1, Int::times)\n\n    override fun visitUnaryExpr(expr: UnaryExpr): BigDecimal {\n        val right = eval(expr.right)\n\n        return when (expr.operator.type) {\n            MINUS -> {\n                right.negate()\n            }\n\n            else -> throw ExpressionException(\"Invalid unary operator\")\n        }\n    }\n\n    override fun visitLeftExpr(expr: LeftExpr): BigDecimal {\n        val left = eval(expr.left)\n\n        return when (expr.operator.type) {\n            FACTORIAL -> left.intValueExact().factorial().toBigDecimal()\n            else -> throw ExpressionException(\"Invalid left operator\")\n        }\n    }\n\n    override fun visitCallExpr(expr: CallExpr): BigDecimal {\n        val name = expr.name\n        val function =\n            functions[name.lowercase()] ?: throw ExpressionException(\"Undefined function '$name'\")\n\n        return function.call(expr.arguments.map { eval(it) })\n    }\n\n    override fun visitLiteralExpr(expr: LiteralExpr): BigDecimal {\n        return expr.value\n    }\n\n    override fun visitVariableExpr(expr: VariableExpr): BigDecimal {\n        val name = expr.name.lexeme\n\n        return variables[name.lowercase()]\n            ?: throw ExpressionException(\"Undefined variable '$name'\")\n    }\n\n    override fun visitGroupingExpr(expr: GroupingExpr): BigDecimal {\n        return eval(expr.expression)\n    }\n\n    private infix fun Expr.or(right: Expr): BigDecimal {\n        val left = eval(this)\n\n        // short-circuit if left is truthy\n        if (left.isTruthy()) return BigDecimal.ONE\n\n        return eval(right).isTruthy().toBigDecimal()\n    }\n\n    private infix fun Expr.and(right: Expr): BigDecimal {\n        val left = eval(this)\n\n        // short-circuit if left is falsey\n        if (!left.isTruthy()) return BigDecimal.ZERO\n\n        return eval(right).isTruthy().toBigDecimal()\n    }\n\n    private fun BigDecimal.isTruthy(): Boolean {\n        return this != BigDecimal.ZERO\n    }\n\n    private fun Boolean.toBigDecimal(): BigDecimal {\n        return if (this) BigDecimal.ONE else BigDecimal.ZERO\n    }\n\n    private infix fun BigDecimal.pow(n: BigDecimal): BigDecimal {\n        var right = n\n        val signOfRight = right.signum()\n        right = right.multiply(signOfRight.toBigDecimal())\n        val remainderOfRight = right.remainder(BigDecimal.ONE)\n        val n2IntPart = right.subtract(remainderOfRight)\n        val intPow = pow(n2IntPart.intValueExact(), mathContext)\n        val doublePow = BigDecimal(toDouble().pow(remainderOfRight.toDouble()))\n\n        var result = intPow.multiply(doublePow, mathContext)\n        if (signOfRight == -1) result = BigDecimal\n            .ONE.divide(result, mathContext.precision, RoundingMode.HALF_UP)\n\n        return result\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/math/evaluator/internal/Expr.kt",
    "content": "package com.infinitepower.newquiz.core.math.evaluator.internal\n\nimport java.math.BigDecimal\n\ninternal sealed class Expr {\n    abstract fun <R> accept(visitor: ExprVisitor<R>): R\n}\n\ninternal class AssignExpr(\n    val name: Token,\n    val value: Expr\n) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitAssignExpr(this)\n    }\n}\n\ninternal class LogicalExpr(\n    val left: Expr,\n    val operator: Token,\n    val right: Expr\n) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitLogicalExpr(this)\n    }\n}\n\ninternal class BinaryExpr(\n    val left: Expr,\n    val operator: Token,\n    val right: Expr\n) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitBinaryExpr(this)\n    }\n}\n\ninternal class UnaryExpr(\n    val operator: Token,\n    val right: Expr\n) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitUnaryExpr(this)\n    }\n}\n\ninternal class LeftExpr(\n    val operator: Token,\n    val left: Expr\n) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitLeftExpr(this)\n    }\n}\n\ninternal class CallExpr(\n    val name: String,\n    val arguments: List<Expr>\n) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitCallExpr(this)\n    }\n}\n\ninternal class LiteralExpr(val value: BigDecimal) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitLiteralExpr(this)\n    }\n}\n\ninternal class VariableExpr(val name: Token) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitVariableExpr(this)\n    }\n}\n\ninternal class GroupingExpr(val expression: Expr) : Expr() {\n    override fun <R> accept(visitor: ExprVisitor<R>): R {\n        return visitor.visitGroupingExpr(this)\n    }\n}\n\ninternal interface ExprVisitor<out R> {\n    fun visitAssignExpr(expr: AssignExpr): R\n\n    fun visitLogicalExpr(expr: LogicalExpr): R\n\n    fun visitBinaryExpr(expr: BinaryExpr): R\n\n    fun visitUnaryExpr(expr: UnaryExpr): R\n\n    fun visitLeftExpr(expr: LeftExpr): R\n\n    fun visitCallExpr(expr: CallExpr): R\n\n    fun visitLiteralExpr(expr: LiteralExpr): R\n\n    fun visitVariableExpr(expr: VariableExpr): R\n\n    fun visitGroupingExpr(expr: GroupingExpr): R\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/math/evaluator/internal/Function.kt",
    "content": "package com.infinitepower.newquiz.core.math.evaluator.internal\n\nimport java.math.BigDecimal\n\nabstract class Function {\n    abstract fun call(arguments: List<BigDecimal>): BigDecimal\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/math/evaluator/internal/Parser.kt",
    "content": "package com.infinitepower.newquiz.core.math.evaluator.internal\n\nimport com.infinitepower.newquiz.core.math.evaluator.ExpressionException\nimport com.infinitepower.newquiz.core.math.evaluator.internal.TokenType.*\nimport java.math.BigDecimal\n\ninternal class Parser(private val tokens: List<Token>) {\n\n    private var current = 0\n\n    fun parse(): Expr {\n        val expr = expression()\n\n        if (!isAtEnd()) {\n            throw ExpressionException(\"Expected end of expression, found '${peek().lexeme}'\")\n        }\n\n        return expr\n    }\n\n    private fun expression(): Expr {\n        return assignment()\n    }\n\n    private fun assignment(): Expr {\n        val expr = or()\n\n        if (match(ASSIGN)) {\n            val value = assignment()\n\n            if (expr is VariableExpr) {\n                val name = expr.name\n\n                return AssignExpr(name, value)\n            } else {\n                throw ExpressionException(\"Invalid assignment target\")\n            }\n        }\n\n        return expr\n    }\n\n    private fun or(): Expr {\n        var expr = and()\n\n        while (match(BAR_BAR)) {\n            val operator = previous()\n            val right = and()\n\n            expr = LogicalExpr(expr, operator, right)\n        }\n\n        return expr\n    }\n\n    private fun and(): Expr {\n        var expr = equality()\n\n        while (match(AMP_AMP)) {\n            val operator = previous()\n            val right = equality()\n\n            expr = LogicalExpr(expr, operator, right)\n        }\n\n        return expr\n    }\n\n    private fun equality(): Expr {\n        var left = comparison()\n\n        while (match(EQUAL_EQUAL, NOT_EQUAL)) {\n            val operator = previous()\n            val right = comparison()\n\n            left = BinaryExpr(left, operator, right)\n        }\n\n        return left\n    }\n\n    private fun comparison(): Expr {\n        var left = addition()\n\n        while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {\n            val operator = previous()\n            val right = addition()\n\n            left = BinaryExpr(left, operator, right)\n        }\n\n        return left\n    }\n\n    private fun addition(): Expr {\n        var left = multiplication()\n\n        while (match(PLUS, MINUS)) {\n            val operator = previous()\n            val right = multiplication()\n\n            left = BinaryExpr(left, operator, right)\n        }\n\n        return left\n    }\n\n    private fun multiplication(): Expr {\n        var left = unary()\n\n        while (match(STAR, SLASH, MODULO)) {\n            val operator = previous()\n            val right = unary()\n\n            left = BinaryExpr(left, operator, right)\n        }\n\n        return left\n    }\n\n    private fun unary(): Expr {\n        if (match(MINUS)) {\n            val operator = previous()\n            val right = unary()\n\n            return UnaryExpr(operator, right)\n        }\n\n        return factorial()\n    }\n\n    private fun factorial(): Expr {\n        val left = exponent()\n\n        if (match(FACTORIAL)) {\n            val operator = previous()\n\n            return LeftExpr(operator, left)\n        }\n\n        return left\n    }\n\n    private fun exponent(): Expr {\n        var left = call()\n\n        if (match(EXPONENT)) {\n            val operator = previous()\n            val right = unary()\n\n            left = BinaryExpr(left, operator, right)\n        }\n\n        return left\n    }\n\n    private fun call(): Expr {\n        if (matchTwo(IDENTIFIER, LEFT_PAREN)) {\n            val (name, _) = previousTwo()\n\n            val arguments = mutableListOf<Expr>()\n\n            if (!check(RIGHT_PAREN)) {\n                do {\n                    arguments += expression()\n                } while (match(COMMA))\n            }\n\n            consume(RIGHT_PAREN, \"Expected ')' after function arguments\")\n\n            return CallExpr(name.lexeme, arguments)\n        }\n\n        return primary()\n    }\n\n    private fun primary(): Expr {\n        if (match(NUMBER)) {\n            return LiteralExpr(previous().literal as BigDecimal)\n        }\n\n        if (match(IDENTIFIER)) {\n            return VariableExpr(previous())\n        }\n\n        if (match(LEFT_PAREN)) {\n            val expr = expression()\n\n            consume(RIGHT_PAREN, \"Expected ')' after '${previous().lexeme}'.\")\n\n            return GroupingExpr(expr)\n        }\n\n        throw ExpressionException(\"Expected expression after '${previous().lexeme}'.\")\n    }\n\n    private fun match(vararg types: TokenType): Boolean {\n        for (type in types) {\n            if (check(type)) {\n                advance()\n\n                return true\n            }\n        }\n\n        return false\n    }\n\n    private fun matchTwo(first: TokenType, second: TokenType): Boolean {\n        val start = current\n\n        if (match(first) && match(second)) {\n            return true\n        }\n\n        current = start\n        return false\n    }\n\n    private fun check(tokenType: TokenType): Boolean {\n        return if (isAtEnd()) {\n            false\n        } else {\n            peek().type === tokenType\n        }\n    }\n\n    private fun consume(type: TokenType, message: String): Token {\n        if (check(type)) return advance()\n\n        throw ExpressionException(message)\n    }\n\n    private fun advance(): Token {\n        if (!isAtEnd()) current++\n\n        return previous()\n    }\n\n    private fun isAtEnd() = peek().type == EOF\n\n    private fun peek() = tokens[current]\n\n    private fun previous() = tokens[current - 1]\n\n    private fun previousTwo() = Pair(tokens[current - 2], tokens[current - 1])\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/math/evaluator/internal/Scanner.kt",
    "content": "package com.infinitepower.newquiz.core.math.evaluator.internal\n\nimport com.infinitepower.newquiz.core.math.evaluator.ExpressionException\nimport com.infinitepower.newquiz.core.math.evaluator.internal.TokenType.*\nimport java.math.MathContext\n\nprivate fun invalidToken(c: Char) {\n    throw ExpressionException(\"Invalid token '$c'\")\n}\n\ninternal class Scanner(\n    private val source: String,\n    private val mathContext: MathContext\n) {\n    private val tokens: MutableList<Token> = mutableListOf()\n    private var start = 0\n    private var current = 0\n\n    fun scanTokens(): List<Token> {\n        while (!isAtEnd()) scanToken()\n\n        tokens.add(Token(EOF, \"\", null))\n        return tokens\n    }\n\n    private fun isAtEnd() = current >= source.length\n\n    private fun scanToken() {\n        start = current\n\n        when (val c = advance()) {\n            ' ',\n            '\\r',\n            '\\t' -> {\n                // Ignore whitespace.\n            }\n\n            '+' -> addToken(PLUS)\n            '-' -> addToken(MINUS)\n            '*' -> addToken(STAR)\n            '/' -> addToken(SLASH)\n            '%' -> addToken(MODULO)\n            '^' -> addToken(EXPONENT)\n            '=' -> if (match('=')) addToken(EQUAL_EQUAL) else addToken(ASSIGN)\n            '!' -> if (match('=')) addToken(NOT_EQUAL) else addToken(FACTORIAL)\n            '>' -> if (match('=')) addToken(GREATER_EQUAL) else addToken(GREATER)\n            '<' -> if (match('=')) addToken(LESS_EQUAL) else addToken(LESS)\n            '|' -> if (match('|')) addToken(BAR_BAR) else invalidToken(c)\n            '&' -> if (match('&')) addToken(AMP_AMP) else invalidToken(c)\n            ',' -> addToken(COMMA)\n            '(' -> addToken(LEFT_PAREN)\n            ')' -> addToken(RIGHT_PAREN)\n            else -> {\n                when {\n                    c.isDigit() -> number()\n                    c.isAlpha() || c.isOtherIdentifiers() -> identifier()\n                    else -> invalidToken(c)\n                }\n            }\n        }\n    }\n\n    private fun isDigit(\n        char: Char,\n        previousChar: Char = '\\u0000',\n        nextChar: Char = '\\u0000'\n    ): Boolean {\n        return char.isDigit() || when (char) {\n            '.' -> true\n            'e', 'E' -> previousChar.isDigit() && (nextChar.isDigit() || nextChar == '+' || nextChar == '-')\n            '+', '-' -> (previousChar == 'e' || previousChar == 'E') && nextChar.isDigit()\n            else -> false\n        }\n    }\n\n    private fun number() {\n        while (peek().isDigit()) advance()\n\n        if (isDigit(peek(), peekPrevious(), peekNext())) {\n            advance()\n            while (isDigit(peek(), peekPrevious(), peekNext())) advance()\n        }\n\n        val value = source\n            .substring(start, current)\n            .toBigDecimal(mathContext)\n\n        addToken(NUMBER, value)\n    }\n\n    private fun identifier() {\n        while (peek().isAlphaNumeric()) advance()\n\n        addToken(IDENTIFIER)\n    }\n\n    private fun advance() = source[current++]\n\n    private fun peek(): Char = if (isAtEnd()) '\\u0000' else source[current]\n\n    private fun peekPrevious(): Char = if (current > 0) source[current - 1] else '\\u0000'\n\n    private fun peekNext(): Char {\n        return if (current + 1 >= source.length) {\n            '\\u0000'\n        } else {\n            source[current + 1]\n        }\n    }\n\n    private fun match(expected: Char): Boolean {\n        if (isAtEnd()) return false\n        if (source[current] != expected) return false\n\n        current++\n        return true\n    }\n\n    private fun addToken(type: TokenType) = addToken(type, null)\n\n    private fun addToken(type: TokenType, literal: Any?) {\n        val text = source.substring(start, current)\n        tokens.add(Token(type, text, literal))\n    }\n\n    private fun Char.isAlphaNumeric() = isAlpha() || isDigit() || isOtherIdentifiers()\n\n    private fun Char.isAlpha() = this in 'a'..'z'\n            || this in 'A'..'Z'\n            || this == '_'\n\n    private fun Char.isOtherIdentifiers() = this == 'π'\n            || this == '√'\n            || this == '!'\n\n    private fun Char.isDigit() = this == '.' || this in '0'..'9'\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/math/evaluator/internal/Token.kt",
    "content": "package com.infinitepower.newquiz.core.math.evaluator.internal\n\ninternal class Token(\n    val type: TokenType,\n    val lexeme: String,\n    val literal: Any?,\n) {\n    override fun toString() = \"$type $lexeme $literal\"\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/math/evaluator/internal/TokenType.kt",
    "content": "package com.infinitepower.newquiz.core.math.evaluator.internal\n\ninternal enum class TokenType {\n    // Basic operators\n    PLUS,\n    MINUS,\n    STAR,\n    SLASH,\n    MODULO,\n    EXPONENT,\n    ASSIGN,\n    FACTORIAL,\n\n    // Logical operators\n    EQUAL_EQUAL,\n    NOT_EQUAL,\n    GREATER,\n    GREATER_EQUAL,\n    LESS,\n    LESS_EQUAL,\n    BAR_BAR,\n    AMP_AMP,\n\n    // Other\n    COMMA,\n\n    // Parentheses\n    LEFT_PAREN,\n    RIGHT_PAREN,\n\n    // Literals\n    NUMBER,\n    IDENTIFIER,\n\n    EOF\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/navigation/MazeNavigator.kt",
    "content": "package com.infinitepower.newquiz.core.navigation\n\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\n\ninterface MazeNavigator {\n    fun navigateToGame(item: MazeQuiz.MazeItem)\n\n    fun navigateToMazeResults(itemId: Int)\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/network/NetworkStatusTracker.kt",
    "content": "package com.infinitepower.newquiz.core.network\n\nimport kotlinx.coroutines.flow.Flow\n\ninterface NetworkStatusTracker {\n    val isOnline: Flow<Boolean>\n\n    fun isCurrentlyConnected(): Boolean\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/network/NetworkStatusTrackerImpl.kt",
    "content": "package com.infinitepower.newquiz.core.network\n\nimport android.content.Context\nimport android.net.ConnectivityManager\nimport android.net.Network\nimport android.net.NetworkCapabilities\nimport android.net.NetworkRequest\nimport android.os.Build\nimport android.util.Log\nimport androidx.core.content.getSystemService\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport kotlinx.coroutines.channels.awaitClose\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.callbackFlow\nimport kotlinx.coroutines.flow.conflate\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass NetworkStatusTrackerImpl @Inject constructor(\n    @ApplicationContext private val context: Context\n) : NetworkStatusTracker {\n    override val isOnline: Flow<Boolean> = callbackFlow {\n        val connectivityManager = context.getSystemService<ConnectivityManager>()\n\n        // Close the flow if connectivityManager is null\n        if (connectivityManager == null) {\n            trySend(false)\n            close()\n            return@callbackFlow\n        }\n\n        /**\n         * The callback's methods are invoked on changes to *any* network matching the [NetworkRequest],\n         * not just the active network. So we can simply track the presence (or absence) of such [Network].\n         */\n        val callback = object : ConnectivityManager.NetworkCallback() {\n            private val networks = mutableSetOf<Network>()\n\n            override fun onAvailable(network: Network) {\n                networks += network\n                trySend(true)\n            }\n\n            override fun onLost(network: Network) {\n                networks -= network\n                // If there are no networks, we're offline\n                trySend(networks.isNotEmpty())\n            }\n        }\n\n        val request = NetworkRequest.Builder()\n            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)\n            .build()\n\n        Log.d(\"NetworkStatusTracker\", \"Registering network callback\")\n        connectivityManager.registerNetworkCallback(request, callback)\n\n        /**\n         * Sends the latest connectivity status to the underlying channel.\n         */\n        trySend(connectivityManager.isCurrentlyConnected())\n\n        /**\n         * When the flow is closed, unregisters the callback.\n         */\n        awaitClose {\n            Log.d(\"NetworkStatusTracker\", \"Unregistering network callback\")\n            connectivityManager.unregisterNetworkCallback(callback)\n        }\n    }.conflate()\n\n    @Suppress(\"DEPRECATION\")\n    private fun ConnectivityManager.isCurrentlyConnected() = when {\n        Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {\n            activeNetwork\n                ?.let(::getNetworkCapabilities)\n                ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)\n        }\n\n        else -> activeNetworkInfo?.isConnected\n    } ?: false\n\n    override fun isCurrentlyConnected(): Boolean {\n        val connectivityManager = context.getSystemService<ConnectivityManager>() ?: return false\n\n        return connectivityManager.isCurrentlyConnected()\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/theme/ExtendedColor.kt",
    "content": "package com.infinitepower.newquiz.core.theme\n\nimport androidx.annotation.Keep\nimport androidx.annotation.Size\nimport androidx.compose.material3.ColorScheme\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.ReadOnlyComposable\nimport androidx.compose.runtime.staticCompositionLocalOf\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.toArgb\nimport com.google.android.material.color.ColorRoles\nimport com.google.android.material.color.MaterialColors\n\n@Keep\n@Immutable\ndata class CustomColor(\n    val key: Key,\n    val originalColor: Color,\n    val harmonize: Boolean = true,\n    val roles: ColorRoles = unspecifiedColorRoles\n) {\n    enum class Key {\n        Blue,\n        Green,\n        Yellow,\n        Red\n    }\n\n    @Keep\n    @Immutable\n    data class ColorRoles(\n        val color: Color,\n        val onColor: Color,\n        val colorContainer: Color,\n        val onColorContainer: Color\n    )\n}\n\nprivate val unspecifiedColorRoles = CustomColor.ColorRoles(\n    color = Color.Unspecified,\n    onColor = Color.Unspecified,\n    colorContainer = Color.Unspecified,\n    onColorContainer = Color.Unspecified,\n)\n\nprivate fun ColorRoles.toColorRoles(): CustomColor.ColorRoles = CustomColor.ColorRoles(\n    color = Color(this.accent),\n    onColor = Color(this.onAccent),\n    colorContainer = Color(this.accentContainer),\n    onColorContainer = Color(this.onAccentContainer),\n)\n\n@Keep\n@Immutable\ndata class ExtendedColors(\n    val colors: List<CustomColor>\n) {\n    fun getColorsByKey(key: CustomColor.Key): CustomColor.ColorRoles {\n        return colors.find { color -> color.key == key }?.roles\n            ?: error(\"No color found for key $key\")\n    }\n\n    fun getColorByKey(key: CustomColor.Key): Color = getColorsByKey(key).color\n    fun getOnColorByKey(key: CustomColor.Key): Color = getColorsByKey(key).onColor\n    fun getColorContainerByKey(key: CustomColor.Key): Color = getColorsByKey(key).colorContainer\n    fun getOnColorContainerByKey(key: CustomColor.Key): Color = getColorsByKey(key).onColorContainer\n}\n\nprivate val initializeExtend = ExtendedColors(\n    listOf(\n        CustomColor(\n            key = CustomColor.Key.Green,\n            originalColor = Color(red = .3f, green = .6f, blue = .3f),\n        ),\n        CustomColor(\n            key = CustomColor.Key.Yellow,\n            originalColor = Color.Yellow,\n        ),\n        CustomColor(\n            key = CustomColor.Key.Red,\n            originalColor = Color.Red,\n        ),\n        CustomColor(\n            key = CustomColor.Key.Blue,\n            originalColor = Color.Blue,\n        ),\n    )\n)\n\nval LocalExtendedColors = staticCompositionLocalOf {\n    initializeExtend\n}\n\nval MaterialTheme.extendedColors: ExtendedColors\n    @Composable\n    @ReadOnlyComposable\n    get() = LocalExtendedColors.current\n\ninternal fun setupCustomColors(\n    colorScheme: ColorScheme,\n    isLight: Boolean\n): ExtendedColors {\n    val colors = initializeExtend.colors.map { customColor ->\n        val shouldHarmonize = customColor.harmonize\n\n        // Harmonize the color if needed, if not, use the original color to get the roles\n        val color = if (shouldHarmonize) {\n            MaterialColors.harmonize(\n                customColor.originalColor.toArgb(),\n                colorScheme.primary.toArgb()\n            )\n        } else {\n            customColor.originalColor.toArgb()\n        }\n\n        val roles = MaterialColors.getColorRoles(color, isLight)\n        customColor.copy(roles = roles.toColorRoles())\n    }\n\n    return ExtendedColors(colors)\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/theme/LocalAnimationsEnabled.kt",
    "content": "package com.infinitepower.newquiz.core.theme\n\nimport androidx.annotation.Keep\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.ReadOnlyComposable\nimport androidx.compose.runtime.compositionLocalOf\n\n@Keep\ndata class AnimationsEnabled(\n    val global: Boolean = true,\n    val wordle: Boolean = true,\n    val multiChoice: Boolean = true\n)\n\nval LocalAnimationsEnabled = compositionLocalOf { AnimationsEnabled() }\n\nval MaterialTheme.animationsEnabled: AnimationsEnabled\n    @Composable\n    @ReadOnlyComposable\n    get() = LocalAnimationsEnabled.current\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/theme/Spacing.kt",
    "content": "package com.infinitepower.newquiz.core.theme\n\nimport androidx.annotation.Keep\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.ReadOnlyComposable\nimport androidx.compose.runtime.staticCompositionLocalOf\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\n\n@Keep\ndata class Spacing(\n    /** 8.dp **/\n    val default: Dp = 8.dp,\n    /** 4.dp **/\n    val extraSmall: Dp = 4.dp,\n    /** 8.dp **/\n    val small: Dp = 8.dp,\n    /** 16.dp **/\n    val medium: Dp = 16.dp,\n    /** 32.dp **/\n    val large: Dp = 32.dp,\n    /** 64.dp **/\n    val extraLarge: Dp = 64.dp,\n)\n\nval LocalSpacing = staticCompositionLocalOf { Spacing() }\n\nval MaterialTheme.spacing: Spacing\n    @Composable\n    @ReadOnlyComposable\n    get() = LocalSpacing.current"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/theme/Theme.kt",
    "content": "package com.infinitepower.newquiz.core.theme\n\nimport android.os.Build\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.compose.material3.ColorScheme\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.darkColorScheme\nimport androidx.compose.material3.dynamicDarkColorScheme\nimport androidx.compose.material3.dynamicLightColorScheme\nimport androidx.compose.material3.lightColorScheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.toArgb\nimport androidx.compose.ui.platform.LocalContext\nimport com.google.android.material.color.MaterialColors\n\nprivate val LightThemeColors = lightColorScheme()\n\nprivate val DarkThemeColors = darkColorScheme()\n\nprivate fun setupErrorColors(\n    colorScheme: ColorScheme,\n    isLight: Boolean\n): ColorScheme {\n    val harmonizedError = MaterialColors.harmonize(colorScheme.error.toArgb(), colorScheme.primary.toArgb())\n    val roles = MaterialColors.getColorRoles(harmonizedError, isLight)\n\n    //returns a colorScheme with newly harmonized error colors\n    return colorScheme.copy(\n        error = Color(roles.accent),\n        onError = Color(roles.onAccent),\n        errorContainer = Color(roles.accentContainer),\n        onErrorContainer = Color(roles.onAccentContainer)\n    )\n}\n\n@Composable\nfun NewQuizTheme(\n    darkTheme: Boolean = isSystemInDarkTheme(),\n    // Dynamic color is available on Android 12+\n    dynamicColor: Boolean = true,\n    lightColorScheme: ColorScheme = LightThemeColors,\n    darkColorScheme: ColorScheme = DarkThemeColors,\n    animationsEnabled: AnimationsEnabled = AnimationsEnabled(),\n    content: @Composable () -> Unit\n) {\n    val colorScheme = when {\n        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {\n            val context = LocalContext.current\n            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)\n        }\n        darkTheme -> darkColorScheme\n        else -> lightColorScheme\n    }\n\n    val colorsWithHarmonizedError = setupErrorColors(colorScheme, !darkTheme)\n    val extendedColors = setupCustomColors(colorScheme, !darkTheme)\n\n    CompositionLocalProvider(\n        LocalSpacing provides Spacing(),\n        LocalExtendedColors provides extendedColors,\n        LocalAnimationsEnabled provides animationsEnabled\n    ) {\n        MaterialTheme(\n            colorScheme = colorsWithHarmonizedError,\n            content = content,\n            typography = AppTypography\n        )\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/theme/Type.kt",
    "content": "package com.infinitepower.newquiz.core.theme\n\nimport androidx.compose.material3.Typography\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.text.font.FontFamily\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.sp\n\nval Roboto = FontFamily.Default\n\nval AppTypography = Typography(\n    displayLarge = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 57.sp,\n        lineHeight = 64.sp,\n        letterSpacing = (-0.25).sp,\n    ),\n    displayMedium = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 45.sp,\n        lineHeight = 52.sp,\n        letterSpacing = 0.sp,\n    ),\n    displaySmall = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 36.sp,\n        lineHeight = 44.sp,\n        letterSpacing = 0.sp,\n    ),\n    headlineLarge = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 32.sp,\n        lineHeight = 40.sp,\n        letterSpacing = 0.sp,\n    ),\n    headlineMedium = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 28.sp,\n        lineHeight = 36.sp,\n        letterSpacing = 0.sp,\n    ),\n    headlineSmall = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 24.sp,\n        lineHeight = 32.sp,\n        letterSpacing = 0.sp,\n    ),\n    titleLarge = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 22.sp,\n        lineHeight = 28.sp,\n        letterSpacing = 0.sp,\n    ),\n    titleMedium = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.Medium,\n        fontSize = 16.sp,\n        lineHeight = 24.sp,\n        letterSpacing = 0.1.sp,\n    ),\n    titleSmall = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.Medium,\n        fontSize = 14.sp,\n        lineHeight = 20.sp,\n        letterSpacing = 0.1.sp,\n    ),\n    labelLarge = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.Medium,\n        fontSize = 14.sp,\n        lineHeight = 20.sp,\n        letterSpacing = 0.1.sp,\n    ),\n    bodyLarge = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 16.sp,\n        lineHeight = 24.sp,\n        letterSpacing = 0.5.sp,\n    ),\n    bodyMedium = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 14.sp,\n        lineHeight = 20.sp,\n        letterSpacing = 0.25.sp,\n    ),\n    bodySmall = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.W400,\n        fontSize = 12.sp,\n        lineHeight = 16.sp,\n        letterSpacing = 0.4.sp,\n    ),\n    labelMedium = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.Medium,\n        fontSize = 12.sp,\n        lineHeight = 16.sp,\n        letterSpacing = 0.5.sp,\n    ),\n    labelSmall = TextStyle(\n        fontFamily = Roboto,\n        fontWeight = FontWeight.Medium,\n        fontSize = 11.sp,\n        lineHeight = 16.sp,\n        letterSpacing = 0.5.sp,\n    ),\n)"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/DisabledEmphasisWrappers.kt",
    "content": "package com.infinitepower.newquiz.core.ui\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.material3.LocalContentColor\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\n\nconst val DISABLED_ALPHA = 0.38f\n\n/**\n * Wraps the content with [CompositionLocalProvider] that overrides [LocalContentColor] with\n * an alpha of [DISABLED_ALPHA] if [enabled] is false. Otherwise, the content is not wrapped and is displayed normally.\n */\n@Composable\nfun DisabledLocalContentEmphasis(\n    enabled: Boolean,\n    content: @Composable () -> Unit\n) {\n    if (enabled) {\n        content()\n    } else {\n        CompositionLocalProvider(\n            LocalContentColor provides LocalContentColor.current.copy(alpha = DISABLED_ALPHA),\n            content = content\n        )\n    }\n}\n\n@Composable\nfun DisabledContentEmphasis(\n    enabled: Boolean,\n    content: @Composable () -> Unit\n) {\n    if (enabled) {\n        content()\n    } else {\n        Box(modifier = Modifier.alpha(DISABLED_ALPHA)) {\n            content()\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/ObserveAsEvents.kt",
    "content": "package com.infinitepower.newquiz.core.ui\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.compose.LocalLifecycleOwner\nimport androidx.lifecycle.repeatOnLifecycle\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.withContext\n\n@Composable\nfun <T> ObserveAsEvents(\n    flow: Flow<T>,\n    vararg keys: Any?,\n    onEvent: (T) -> Unit\n) {\n    val lifecycleOwner = LocalLifecycleOwner.current\n\n    LaunchedEffect(lifecycleOwner.lifecycle, *keys, flow) {\n        lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {\n            withContext(Dispatchers.Main.immediate) {\n                flow.collect(onEvent)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/SnackbarController.kt",
    "content": "package com.infinitepower.newquiz.core.ui\n\nimport androidx.compose.material3.SnackbarDuration\nimport com.infinitepower.newquiz.model.UiText\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.flow.receiveAsFlow\n\nobject SnackbarController {\n    private val _events = Channel<SnackbarEvent>()\n    val events = _events.receiveAsFlow()\n\n    suspend fun sendEvent(event: SnackbarEvent) {\n        _events.send(event)\n    }\n\n    suspend fun sendShortMessage(message: UiText) {\n        sendEvent(event = SnackbarEvent(message))\n    }\n\n    /**\n     * Show a short message to the user.\n     */\n    suspend fun sendShortMessage(message: String) {\n        sendEvent(event = SnackbarEvent(UiText.DynamicString(message)))\n    }\n}\n\ndata class SnackbarEvent(\n    val message: UiText,\n    val action: SnackbarAction? = null,\n    val withDismissAction: Boolean = false,\n    val duration: SnackbarDuration = if (action == null) SnackbarDuration.Short else SnackbarDuration.Indefinite\n)\n\ndata class SnackbarAction(\n    val name: UiText,\n    val action: () -> Unit\n)\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/components/AppNameWithLogo.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components\n\nimport androidx.compose.animation.core.animateFloatAsState\nimport androidx.compose.animation.core.tween\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.interaction.collectIsPressedAsState\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.material3.Badge\nimport androidx.compose.material3.BadgedBox\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.res.painterResource\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.util.rememberAppVersion\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\nfun AppNameWithLogo(\n    modifier: Modifier = Modifier\n) {\n    val appVersion = rememberAppVersion()\n\n    AppNameWithLogo(\n        modifier = modifier,\n        logoContent = { AppLogo() },\n        appNameWithVersion = {\n            AppNameWithVersion(\n                appName = stringResource(id = CoreR.string.app_name),\n                appVersion = appVersion\n            )\n        }\n    )\n}\n\n@Composable\ninternal fun AppNameWithLogo(\n    modifier: Modifier = Modifier,\n    logoContent: @Composable () -> Unit,\n    appNameWithVersion: @Composable () -> Unit\n) {\n    Column(\n        modifier = modifier.fillMaxWidth(),\n        horizontalAlignment = Alignment.CenterHorizontally\n    ) {\n        logoContent()\n        Spacer(modifier = Modifier.height(MaterialTheme.spacing.large))\n        appNameWithVersion()\n    }\n}\n\n@Composable\nprivate fun AppNameWithVersion(\n    modifier: Modifier = Modifier,\n    appName: String,\n    appVersion: String\n) {\n    BadgedBox(\n        badge = {\n            Badge {\n                Text(text = appVersion)\n            }\n        },\n        modifier = modifier\n    ) {\n        Text(\n            text = appName,\n            style = MaterialTheme.typography.headlineMedium\n        )\n    }\n}\n\n@Composable\nprivate fun AppLogo(\n    modifier: Modifier = Modifier,\n    color: Color = MaterialTheme.colorScheme.primary,\n    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },\n    onClick: () -> Unit = {}\n) {\n    val isPressed by interactionSource.collectIsPressedAsState()\n\n    val animatedCurve by animateFloatAsState(\n        targetValue = if (isPressed) 0f else 0.05f,\n        animationSpec = tween(durationMillis = 500),\n        label = \"Curve\"\n    )\n\n    val roundedPolygonShape = RoundedPolygonShape(\n        sides = 12,\n        curve = animatedCurve.toDouble()\n    )\n\n    Surface(\n        modifier = modifier.size(240.dp),\n        color = color,\n        shape = roundedPolygonShape,\n        interactionSource = interactionSource,\n        onClick = onClick\n    ) {\n        Icon(\n            painter = painterResource(id = CoreR.drawable.logo_monochromatic),\n            contentDescription = stringResource(id = CoreR.string.app_name),\n            modifier = Modifier.size(90.dp)\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun AppNameWithLogoPreview() {\n    NewQuizTheme {\n        Surface {\n            AppNameWithLogo(\n                modifier = Modifier.padding(16.dp),\n                logoContent = { AppLogo() },\n                appNameWithVersion = {\n                    AppNameWithVersion(\n                        appName = stringResource(id = CoreR.string.app_name),\n                        appVersion = \"1.0.0\"\n                    )\n                }\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/components/RemainingTimeComponent.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components\n\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.animation.animateColorAsState\nimport androidx.compose.animation.core.animateFloatAsState\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.LocalContentColor\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.ProgressIndicatorDefaults\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.surfaceColorAtElevation\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.derivedStateOf\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.StrokeCap\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.model.RemainingTime\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.seconds\n\n/**\n * Component that displays the remaining time of a quiz.\n *\n * @param modifier Modifier to be applied to the component.\n * @param remainingTime The remaining time to be displayed.\n * @param maxTime The maximum time of the quiz.\n * @param warningTime The time at which the text starts blinking.\n * @param showProgressIndicator Whether to show the progress indicator or not.\n */\n@Composable\nfun RemainingTimeComponent(\n    modifier: Modifier = Modifier,\n    remainingTime: RemainingTime,\n    maxTime: Duration,\n    warningTime: Duration = 10.seconds,\n    showProgressIndicator: Boolean = true,\n    animationsEnabled: Boolean = true\n) {\n    val animatedProgressValue by animateFloatAsState(\n        targetValue = remainingTime.getRemainingPercent(maxTime).toFloat(),\n        animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,\n        label = \"Animated progress value\"\n    )\n\n    // If the remaining time is less than the warning time, the text starts animating.\n    val isWarningTime by remember(remainingTime) {\n        derivedStateOf {\n            animationsEnabled && remainingTime.value <= warningTime\n        }\n    }\n\n    val progressColor by animateColorAsState(\n        targetValue = if (isWarningTime) {\n            MaterialTheme.colorScheme.error\n        } else {\n            MaterialTheme.colorScheme.primary\n        },\n        label = \"Animated progress color\"\n    )\n\n    val trackProgressColor by animateColorAsState(\n        targetValue = if (isWarningTime) {\n            MaterialTheme.colorScheme.error.copy(alpha = 0.2f)\n        } else {\n            MaterialTheme.colorScheme.surfaceColorAtElevation(8.dp)\n        },\n        label = \"Animated track progress color\"\n    )\n\n    Box(\n        contentAlignment = Alignment.Center,\n        modifier = modifier\n    ) {\n        if (showProgressIndicator) {\n            CircularProgressIndicator(\n                modifier = Modifier\n                    .size(75.dp)\n                    .testTag(RemainingTimeComponentTestTags.PROGRESS_INDICATOR),\n                progress = { animatedProgressValue },\n                color = progressColor,\n                trackColor = trackProgressColor,\n                strokeCap = StrokeCap.Round,\n            )\n        }\n\n        if (isWarningTime) {\n            AnimatedRemainingTimeText(remainingTime = remainingTime)\n        } else {\n            RemainingTimeText(remainingTime = remainingTime.toMinuteSecondFormatted())\n        }\n\n    }\n}\n\n/**\n * Component that displays the text of the remaining time of a quiz.\n *\n * @param modifier Modifier to be applied to the component.\n */\n@Composable\nprivate fun RemainingTimeText(\n    modifier: Modifier = Modifier,\n    remainingTime: String,\n    style: TextStyle = MaterialTheme.typography.titleMedium,\n    color: Color = LocalContentColor.current\n) {\n    Text(\n        text = remainingTime,\n        textAlign = TextAlign.Center,\n        style = style,\n        modifier = modifier,\n        color = color\n    )\n}\n\n@Composable\nprivate fun AnimatedRemainingTimeText(\n    modifier: Modifier = Modifier,\n    remainingTime: RemainingTime\n) {\n    val remainingTimeInSeconds by remember(remainingTime) {\n        derivedStateOf {\n            remainingTime.value.inWholeSeconds\n        }\n    }\n\n    RemainingTimeText(\n        modifier = modifier,\n        remainingTime = remainingTimeInSeconds.toString(),\n        style = MaterialTheme.typography.titleLarge,\n        color = MaterialTheme.colorScheme.error\n    )\n\n    /* Crash problem?\n\n    AnimatedContent(\n        modifier = modifier,\n        targetState = remainingTimeInSeconds,\n        transitionSpec = {\n            // Compare the incoming number with the previous number.\n            if (remainingTimeInSeconds > initialState) {\n                // If the target number is larger, it slides up and fades in\n                // while the initial (smaller) number slides up and fades out.\n                slideInVertically { height -> height } + fadeIn() togetherWith\n                        slideOutVertically { height -> -height } + fadeOut()\n            } else {\n                // If the target number is smaller, it slides down and fades in\n                // while the initial number slides down and fades out.\n                slideInVertically { height -> -height } + fadeIn() togetherWith\n                        slideOutVertically { height -> height } + fadeOut()\n            }.using(\n                // Disable clipping since the faded slide-in/out should\n                // be displayed out of bounds.\n                SizeTransform(clip = false)\n            )\n        },\n        label = \"Pulsating remaining time text\"\n    ) { targetRemainingTime ->\n        RemainingTimeText(\n            remainingTime = targetRemainingTime.toString(),\n            style = MaterialTheme.typography.titleLarge,\n            color = MaterialTheme.colorScheme.error\n        )\n    }\n\n     */\n}\n\n@VisibleForTesting\ninternal object RemainingTimeComponentTestTags {\n    const val PROGRESS_INDICATOR = \"PROGRESS_INDICATOR\"\n}\n\n@Composable\n@PreviewLightDark\nprivate fun RemainingTimeComponentPreview() {\n    NewQuizTheme {\n        Surface {\n            RemainingTimeComponent(\n                modifier = Modifier.padding(16.dp),\n                remainingTime = RemainingTime(5.seconds),\n                maxTime = 30.seconds\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/components/RoundedPolygonShape.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components\n\nimport androidx.compose.ui.geometry.Size\nimport androidx.compose.ui.graphics.Outline\nimport androidx.compose.ui.graphics.Path\nimport androidx.compose.ui.graphics.Shape\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.LayoutDirection\nimport kotlin.math.PI\nimport kotlin.math.cos\nimport kotlin.math.min\nimport kotlin.math.sin\n\n/**\n * Shape describing star with rounded corners\n *\n * Note: The shape draws within the minimum of provided width and height so can't be used to create stretched shape.\n *\n * @param sides number of sides.\n * @param curve a double value between 0.0 - 1.0 for modifying star curve.\n * @param rotation  value between 0 - 360\n * @param iterations a value between 0 - 360 that determines the quality of star shape.\n * @author pz64\n */\nclass RoundedPolygonShape(\n    private val sides: Int,\n    private val curve: Double = 0.09,\n    private val rotation: Float = 0f,\n    private val iterations: Int = 360\n) : Shape {\n    private companion object {\n        const val TWO_PI = 2 * PI\n    }\n\n    private val steps = (TWO_PI) / min(iterations, 360)\n    private val rotationDegree = (PI / 180) * rotation\n\n    override fun createOutline(\n        size: Size,\n        layoutDirection: LayoutDirection,\n        density: Density\n    ): Outline = Outline.Generic(Path().apply {\n        val r = min(size.height, size.width) * 0.4 * mapRange(1.0, 0.0, 0.5, 1.0, curve)\n\n        val xCenter = size.width * .5f\n        val yCenter = size.height * .5f\n\n        moveTo(xCenter, yCenter)\n\n        var t = 0.0\n\n        while (t <= TWO_PI) {\n            val x = r * (cos(t - rotationDegree) * (1 + curve * cos(sides * t)))\n            val y = r * (sin(t - rotationDegree) * (1 + curve * cos(sides * t)))\n            lineTo((x + xCenter).toFloat(), (y + yCenter).toFloat())\n\n            t += steps\n        }\n\n        val x = r * (cos(t - rotationDegree) * (1 + curve * cos(sides * t)))\n        val y = r * (sin(t - rotationDegree) * (1 + curve * cos(sides * t)))\n        lineTo((x + xCenter).toFloat(), (y + yCenter).toFloat())\n\n    })\n\n    private fun mapRange(a: Double, b: Double, c: Double, d: Double, x: Double): Double {\n        return (x - a) / (b - a) * (d - c) + c\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/components/category/CategoryBadge.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.category\n\nimport androidx.compose.animation.AnimatedVisibility\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material.icons.rounded.Wifi\nimport androidx.compose.material.icons.rounded.WifiOff\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Shape\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\n\n/**\n * A badge that indicates that the category requires an internet connection or not.\n * When clicked, it shows a text that explains its meaning.\n *\n * @param modifier The modifier to be applied to the component.\n * @param requireConnection Whether the category requires an internet connection or not.\n * @param showTextByDefault Whether the text should be shown by default.\n */\n@Composable\ninternal fun CategoryConnectionInfoBadge(\n    modifier: Modifier = Modifier,\n    requireConnection: Boolean = true,\n    showTextByDefault: Boolean = false,\n) {\n    val (showText, setShowText) = remember {\n        mutableStateOf(showTextByDefault)\n    }\n\n    val description = stringResource(\n        id = if (requireConnection) {\n            R.string.require_internet_connection\n        } else {\n            R.string.dont_require_internet_connection\n        }\n    )\n\n    CategoryBadge(\n        modifier = modifier,\n        onClick = { setShowText(!showText) }\n    ) {\n        Row(\n            modifier = Modifier\n                .padding(MaterialTheme.spacing.default)\n                .semantics(\n                    mergeDescendants = true,\n                ) {\n                    contentDescription = description\n                },\n            verticalAlignment = Alignment.CenterVertically,\n            horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.default)\n        ) {\n            Icon(\n                imageVector = if (requireConnection) {\n                    Icons.Rounded.Wifi\n                } else {\n                    Icons.Rounded.WifiOff\n                },\n                contentDescription = null\n            )\n            AnimatedVisibility(\n                visible = showText,\n                label = \"Connection info text\"\n            ) {\n                Text(\n                    text = description,\n                    style = MaterialTheme.typography.bodySmall\n                )\n            }\n        }\n    }\n}\n\n/**\n * A badge that indicates that the category is checked.\n */\n@Composable\ninternal fun CategoryCheckedBadge(\n    modifier: Modifier = Modifier,\n    onClick: () -> Unit\n) {\n    CategoryBadge(\n        modifier = modifier,\n        color = MaterialTheme.colorScheme.primary,\n        onClick = onClick\n    ) {\n        Icon(\n            imageVector = Icons.Rounded.Check,\n            contentDescription = null,\n            modifier = Modifier.padding(MaterialTheme.spacing.default)\n        )\n    }\n}\n\n@Composable\nprivate fun CategoryBadge(\n    modifier: Modifier = Modifier,\n    color: Color = MaterialTheme.colorScheme.tertiaryContainer,\n    shape: Shape = MaterialTheme.shapes.medium,\n    onClick: () -> Unit = {},\n    badgeContent: @Composable () -> Unit\n) {\n    Surface(\n        modifier = modifier,\n        color = color,\n        shape = shape,\n        onClick = onClick,\n        content = badgeContent\n    )\n}\n\n@Composable\n@PreviewLightDark\nprivate fun CategoryConnectionInfoBadgePreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) requireConnection: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            CategoryConnectionInfoBadge(\n                modifier = Modifier.padding(16.dp),\n                requireConnection = requireConnection\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun CategoryCheckedBadgePreview() {\n    NewQuizTheme {\n        Surface {\n            CategoryCheckedBadge(\n                modifier = Modifier.padding(16.dp),\n                onClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/components/category/CategoryComponent.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.category\n\nimport androidx.compose.animation.AnimatedVisibility\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Shape\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport coil.compose.AsyncImage\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.DISABLED_ALPHA\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\n\n@Composable\nfun CategoryComponent(\n    modifier: Modifier = Modifier,\n    title: String,\n    imageUrl: Any,\n    requireInternetConnection: Boolean = false,\n    showConnectionInfo: ShowCategoryConnectionInfo = ShowCategoryConnectionInfo.NONE,\n    checked: Boolean = false,\n    enabled: Boolean = true,\n    clickEnabled: Boolean = enabled,\n    textStyle: TextStyle = MaterialTheme.typography.headlineLarge,\n    shape: Shape = MaterialTheme.shapes.large,\n    onClick: () -> Unit = {},\n    onCheckClick: () -> Unit = {}\n) {\n    val containerOverlayColor = if (isSystemInDarkTheme()) {\n        MaterialTheme.colorScheme.primaryContainer\n    } else {\n        MaterialTheme.colorScheme.primary\n    }.copy(alpha = if (checked) 0.6f else 0.5f)\n\n    val textColor = Color.White.copy(\n        alpha = if (enabled) 1f else DISABLED_ALPHA\n    )\n\n    Surface(\n        modifier = modifier.height(120.dp),\n        shape = shape,\n        onClick = onClick,\n        enabled = enabled && clickEnabled,\n        border = if (checked) {\n            BorderStroke(2.dp, MaterialTheme.colorScheme.primary)\n        } else {\n            null\n        }\n    ) {\n        AsyncImage(\n            model = imageUrl,\n            contentDescription = stringResource(id = R.string.image_category_of_s, title),\n            modifier = Modifier\n                .fillMaxSize()\n                .clip(MaterialTheme.shapes.medium)\n                .alpha(if (enabled) 1f else DISABLED_ALPHA),\n            contentScale = ContentScale.Crop\n        )\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(containerOverlayColor),\n            contentAlignment = Alignment.Center\n        ) {\n            Text(\n                text = title,\n                style = textStyle,\n                color = textColor,\n                textAlign = TextAlign.Center,\n                modifier = Modifier.padding(horizontal = MaterialTheme.spacing.large)\n            )\n\n            Row(\n                modifier = Modifier\n                    .padding(MaterialTheme.spacing.default)\n                    .align(Alignment.TopEnd),\n            ) {\n                if (showConnectionInfo.shouldShowBadge(requireInternetConnection)) {\n                    CategoryConnectionInfoBadge(\n                        requireConnection = requireInternetConnection\n                    )\n                }\n                AnimatedVisibility(\n                    visible = checked,\n                    label = \"Checked badge\"\n                ) {\n                    CategoryCheckedBadge(\n                        onClick = onCheckClick\n                    )\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun CategoryPreview() {\n    NewQuizTheme {\n        Surface {\n            CategoryComponent(\n                title = \"Title\",\n                imageUrl = \"\",\n                modifier = Modifier\n                    .padding(16.dp)\n                    .fillMaxWidth(),\n                requireInternetConnection = true,\n                showConnectionInfo = ShowCategoryConnectionInfo.BOTH,\n                checked = true\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/components/icon/button/BackIconButton.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.icon.button\n\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.automirrored.rounded.ArrowBack\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport com.infinitepower.newquiz.core.R\n\n@Composable\nfun BackIconButton(\n    modifier: Modifier = Modifier,\n    onClick: () -> Unit\n) {\n    IconButton(\n        onClick = onClick,\n        modifier = modifier\n    ) {\n        Icon(\n            imageVector = Icons.AutoMirrored.Rounded.ArrowBack,\n            contentDescription = stringResource(id = R.string.back)\n        )\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/components/skip_question/SkipButton.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.skip_question\n\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.SkipNext\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport com.infinitepower.newquiz.core.R\n\n@Composable\nfun SkipIconButton(\n    modifier: Modifier = Modifier,\n    onClick: () -> Unit\n) {\n    IconButton(\n        modifier = modifier,\n        onClick = onClick\n    ) {\n        Icon(\n            imageVector = Icons.Rounded.SkipNext,\n            contentDescription = stringResource(id = R.string.skip)\n        )\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/components/skip_question/SkipQuestionDialog.kt",
    "content": "package com.infinitepower.newquiz.core.ui.components.skip_question\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.BasicAlertDialog\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.pluralStringResource\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.window.DialogProperties\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ExperimentalMaterial3Api\nfun SkipQuestionDialog(\n    userDiamonds: Int,\n    skipCost: Int,\n    loading: Boolean = false,\n    onSkipClick: () -> Unit,\n    onDismissClick: () -> Unit\n) {\n    if (loading) {\n        LoadingDialog()\n    } else if (userDiamonds >= 0) {\n        // If user diamonds is negative, it means that the user has already skipped the question or\n        // the user didn't request to skip the question yet.\n        SkipQuestionDialog(\n            userDiamonds = userDiamonds,\n            skipCost = skipCost,\n            onSkipClick = onSkipClick,\n            onDismissClick = onDismissClick\n        )\n    }\n}\n\n@Composable\nprivate fun SkipQuestionDialog(\n    userDiamonds: Int,\n    skipCost: Int,\n    onSkipClick: () -> Unit,\n    onDismissClick: () -> Unit\n) {\n    val canSkip = remember(userDiamonds, skipCost) {\n        userDiamonds >= skipCost\n    }\n\n    if (canSkip) {\n        AlertDialog(\n            onDismissRequest = onDismissClick,\n            title = { Text(text = stringResource(id = CoreR.string.skip_question_q)) },\n            text = {\n                Text(\n                    text = pluralStringResource(\n                        id = CoreR.plurals.you_have_n_diamonds_skip_question_q,\n                        count = userDiamonds,\n                        formatArgs = arrayOf(userDiamonds, skipCost)\n                    )\n                )\n            },\n            confirmButton = {\n                TextButton(\n                    onClick = {\n                        onSkipClick()\n                        onDismissClick()\n                    }\n                ) {\n                    Text(text = stringResource(id = CoreR.string.skip))\n                }\n            },\n            dismissButton = {\n                TextButton(onClick = onDismissClick) {\n                    Text(text = stringResource(id = CoreR.string.close))\n                }\n            }\n        )\n    } else {\n        NoDiamondsDialog(onDismissClick = onDismissClick)\n    }\n}\n\n@Composable\nprivate fun NoDiamondsDialog(\n    onDismissClick: () -> Unit\n) {\n    AlertDialog(\n        onDismissRequest = onDismissClick,\n        title = { Text(text = stringResource(id = R.string.no_diamonds)) },\n        text = {\n            Text(\n                text = stringResource(id = R.string.no_diamonds_to_skip_question_description)\n            )\n        },\n        confirmButton = {\n            TextButton(onClick = onDismissClick) {\n                Text(text = stringResource(id = R.string.close))\n            }\n        }\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\nprivate fun LoadingDialog() {\n    BasicAlertDialog(\n        onDismissRequest = {},\n        properties = DialogProperties(\n            dismissOnBackPress = false,\n            dismissOnClickOutside = false\n        )\n    ) {\n        Surface(\n            modifier = Modifier\n                .height(200.dp)\n                .fillMaxWidth(),\n            shape = MaterialTheme.shapes.medium,\n            tonalElevation = 6.dp\n        ) {\n            Column(\n                modifier = Modifier\n                    .fillMaxSize()\n                    .padding(MaterialTheme.spacing.medium),\n                horizontalAlignment = Alignment.CenterHorizontally,\n                verticalArrangement = Arrangement.Center\n            ) {\n                Text(\n                    text = \"Loading your diamonds...\",\n                    style = MaterialTheme.typography.headlineMedium,\n                    textAlign = TextAlign.Center\n                )\n                Spacer(modifier = Modifier.height(MaterialTheme.spacing.large))\n                CircularProgressIndicator()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home/ExpandCategoriesButton.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home\n\nimport androidx.compose.animation.animateContentSize\nimport androidx.compose.animation.core.animateFloat\nimport androidx.compose.animation.core.updateTransition\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.ExpandMore\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.graphicsLayer\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\n\n@Composable\ninternal fun ExpandCategoriesButton(\n    modifier: Modifier = Modifier,\n    seeAllCategories: Boolean,\n    onSeeAllCategoriesClick: () -> Unit\n) {\n    val transition = updateTransition(\n        targetState = seeAllCategories,\n        label = \"Expand Categories Button\"\n    )\n\n    val seeAllText = if (seeAllCategories) {\n        stringResource(id = R.string.see_less_categories)\n    } else {\n        stringResource(id = R.string.see_all_categories)\n    }\n\n    // Rotates the icon when the button is pressed\n    val rotation by transition.animateFloat(\n        label = \"Icon Rotation\",\n    ) { state ->\n        if (state) 180f else 0f\n    }\n\n    Box(\n        modifier = modifier,\n        contentAlignment = Alignment.Center\n    ) {\n        TextButton(onClick = onSeeAllCategoriesClick) {\n            Icon(\n                imageVector = Icons.Rounded.ExpandMore,\n                contentDescription = seeAllText,\n                modifier = Modifier\n                    .size(ButtonDefaults.IconSize)\n                    .graphicsLayer {\n                        rotationZ = rotation\n                    }\n            )\n            Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))\n            Text(\n                text = seeAllText,\n                style = MaterialTheme.typography.bodyMedium,\n                modifier = Modifier.animateContentSize()\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun ExpandCategoriesButtonPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) seeAllCategories: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            ExpandCategoriesButton(\n                seeAllCategories = seeAllCategories,\n                onSeeAllCategoriesClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home/HomeCategoriesItems.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyListScope\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.ui.components.category.CategoryComponent\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\n\nfun <T : BaseCategory> LazyListScope.homeCategoriesItems(\n    contentPadding: PaddingValues = PaddingValues(),\n    seeAllCategories: Boolean,\n    recentCategories: List<T>,\n    otherCategories: List<T>,\n    isInternetAvailable: Boolean,\n    showConnectionInfo: ShowCategoryConnectionInfo,\n    onCategoryClick: (T) -> Unit,\n    onSeeAllCategoriesClick: () -> Unit,\n) {\n    if (recentCategories.isEmpty() && otherCategories.isEmpty()) {\n        item {\n            Box(\n                modifier = Modifier.fillParentMaxWidth(),\n                contentAlignment = Alignment.Center\n            ) {\n                Text(\n                    text = stringResource(id = R.string.no_categories_available),\n                    style = MaterialTheme.typography.bodyMedium\n                )\n            }\n        }\n    }\n\n    items(\n        items = recentCategories,\n        key = { category -> \"recent_category_${category.id}\" }\n    ) { category ->\n        CategoryComponent(\n            modifier = Modifier\n                .fillParentMaxWidth()\n                .height(120.dp)\n                .padding(contentPadding),\n            title = category.name.asString(),\n            imageUrl = category.image,\n            onClick = { onCategoryClick(category) },\n            enabled = isInternetAvailable || !category.requireInternetConnection,\n            requireInternetConnection = category.requireInternetConnection,\n            showConnectionInfo = showConnectionInfo\n        )\n    }\n\n    if (recentCategories.isNotEmpty() && otherCategories.isNotEmpty()) {\n        item(\n            key = \"see_all_categories_button\",\n        ) {\n            ExpandCategoriesButton(\n                modifier = Modifier.fillParentMaxWidth(),\n                seeAllCategories = seeAllCategories,\n                onSeeAllCategoriesClick = onSeeAllCategoriesClick\n            )\n        }\n    }\n\n    if (seeAllCategories) {\n        items(\n            items = otherCategories,\n            key = { category -> category.id }\n        ) { category ->\n            CategoryComponent(\n                modifier = Modifier\n                    .fillParentMaxWidth()\n                    .height(120.dp)\n                    .padding(contentPadding),\n                title = category.name.asString(),\n                imageUrl = category.image,\n                onClick = { onCategoryClick(category) },\n                enabled = isInternetAvailable || !category.requireInternetConnection,\n                requireInternetConnection = category.requireInternetConnection,\n                showConnectionInfo = showConnectionInfo\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home/HomeLazyColumn.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.LazyListScope\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport com.infinitepower.newquiz.core.theme.spacing\n\n@Composable\nfun HomeLazyColumn(\n    modifier: Modifier = Modifier,\n    verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(MaterialTheme.spacing.medium),\n    contentPadding: PaddingValues = PaddingValues(\n        top = MaterialTheme.spacing.medium,\n        start = MaterialTheme.spacing.medium,\n        end = MaterialTheme.spacing.medium,\n        bottom = MaterialTheme.spacing.large,\n    ),\n    content: LazyListScope.() -> Unit\n) {\n    LazyColumn(\n        modifier = modifier.fillMaxSize(),\n        verticalArrangement = verticalArrangement,\n        contentPadding = contentPadding,\n        content = content\n    )\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/HomeListContent.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.home_card.components.HomeCardItemContent\nimport com.infinitepower.newquiz.core.ui.home_card.model.HomeCardItem\n\n@Composable\n@ExperimentalMaterial3Api\nfun HomeListContent(\n    items: List<HomeCardItem>\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    LazyColumn(\n        contentPadding = PaddingValues(spaceMedium),\n        verticalArrangement = Arrangement.spacedBy(spaceMedium)\n    ) {\n        items(\n            items = items,\n            key = { it.getId() },\n        ) { item ->\n            HomeCardItemContent(\n                modifier = Modifier.fillParentMaxWidth(),\n                item = item\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/components/HomeCardIcon.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card.components\n\nimport androidx.compose.material3.Icon\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Modifier\nimport com.airbnb.lottie.compose.LottieAnimation\nimport com.airbnb.lottie.compose.LottieConstants\nimport com.airbnb.lottie.compose.rememberLottieComposition\nimport com.infinitepower.newquiz.core.ui.home_card.model.CardIcon\n\n@Composable\ninternal fun HomeCardIcon(\n    modifier: Modifier = Modifier,\n    icon: CardIcon,\n    contentDescription: String\n) {\n    when (icon) {\n        is CardIcon.Icon -> {\n            Icon(\n                imageVector = icon.vector,\n                contentDescription = contentDescription,\n                modifier = modifier\n            )\n        }\n        is CardIcon.Lottie -> {\n            val composition by rememberLottieComposition(spec = icon.spec)\n\n            LottieAnimation(\n                composition = composition,\n                modifier = modifier,\n                iterations = LottieConstants.IterateForever,\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/components/HomeCardItemContent.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card.components\n\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport com.infinitepower.newquiz.core.ui.home_card.model.HomeCardItem\n\n@Composable\n@ExperimentalMaterial3Api\nfun HomeCardItemContent(\n    modifier: Modifier = Modifier,\n    item: HomeCardItem,\n) {\n    when (item) {\n        is HomeCardItem.GroupTitle -> {\n            HomeGroupTitle(\n                modifier = modifier,\n                data = item\n            )\n        }\n        is HomeCardItem.LargeCard -> {\n            HomeLargeCard(\n                modifier = modifier,\n                data = item\n            )\n        }\n        is HomeCardItem.MediumCard -> {\n            HomeMediumCard(\n                modifier = modifier,\n                data = item\n            )\n        }\n        is HomeCardItem.HorizontalItems<*> -> {\n            HomeHorizontalItems(\n                modifier = modifier,\n                item = item,\n                itemContent = item.itemContent\n            )\n        }\n        is HomeCardItem.CustomItem -> item.content()\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/components/HomeGroupTitle.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card.components\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.home_card.model.HomeCardItem\n\n@Composable\nfun HomeGroupTitle(\n    modifier: Modifier = Modifier,\n    data: HomeCardItem.GroupTitle\n) {\n    HomeGroupTitle(\n        modifier = modifier,\n        title = stringResource(id = data.title)\n    )\n}\n\n@Composable\nfun HomeGroupTitle(\n    modifier: Modifier = Modifier,\n    title: String\n) {\n    Surface(\n        modifier = modifier\n    ) {\n        Text(\n            text = title,\n            style = MaterialTheme.typography.headlineMedium,\n            modifier = Modifier.padding(vertical = MaterialTheme.spacing.medium)\n        )\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/components/HomeHorizontalItems.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.lazy.LazyRow\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.home_card.model.HomeCardItem\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun <T : Any> HomeHorizontalItems(\n    modifier: Modifier = Modifier,\n    item: HomeCardItem.HorizontalItems<T>,\n    itemContent: @Composable (item: T) -> Unit\n) {\n    LazyRow(\n        verticalAlignment = Alignment.CenterVertically,\n        horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.medium),\n        modifier = modifier\n    ) {\n        items(items = item.items) { item ->\n            itemContent(item)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/components/HomeLargeCard.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material3.Card\nimport androidx.compose.material3.CardColors\nimport androidx.compose.material3.CardDefaults\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.dp\nimport com.airbnb.lottie.compose.LottieAnimation\nimport com.airbnb.lottie.compose.LottieConstants\nimport com.airbnb.lottie.compose.rememberLottieComposition\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.DISABLED_ALPHA\nimport com.infinitepower.newquiz.core.ui.home_card.model.CardIcon\nimport com.infinitepower.newquiz.core.ui.home_card.model.HomeCardItem\n\n@Composable\n@ExperimentalMaterial3Api\nfun HomeLargeCard(\n    modifier: Modifier = Modifier,\n    data: HomeCardItem.LargeCard\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val title = stringResource(id = data.title)\n\n    val cardColors = if (data.backgroundPrimary) {\n        getPrimaryCardColors()\n    } else {\n        CardDefaults.cardColors()\n    }\n\n    Card(\n        onClick = data.onClick,\n        enabled = data.enabled,\n        modifier = modifier,\n        colors = cardColors\n    ) {\n        Column(\n            modifier = Modifier.padding(spaceMedium),\n        ) {\n            Text(\n                text = title,\n                style = MaterialTheme.typography.titleMedium,\n                modifier = Modifier.fillMaxWidth()\n            )\n            Spacer(modifier = Modifier.height(spaceMedium))\n            Row(\n                modifier = Modifier.align(Alignment.End),\n                verticalAlignment = Alignment.CenterVertically,\n                horizontalArrangement = Arrangement.spacedBy(spaceMedium)\n            ) {\n                when (data.icon) {\n                    is CardIcon.Icon -> {\n                        Icon(\n                            imageVector = data.icon.vector,\n                            contentDescription = title,\n                            modifier = Modifier.size(100.dp)\n                        )\n                    }\n                    is CardIcon.Lottie -> {\n                        val composition by rememberLottieComposition(spec = data.icon.spec)\n\n                        LottieAnimation(\n                            composition = composition,\n                            modifier = Modifier\n                                .size(100.dp)\n                                .alpha(if (data.enabled) 1f else DISABLED_ALPHA),\n                            iterations = LottieConstants.IterateForever,\n                            isPlaying = data.enabled\n                        )\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun getPrimaryCardColors(): CardColors = CardDefaults.cardColors(\n    containerColor = MaterialTheme.colorScheme.primary,\n    contentColor = MaterialTheme.colorScheme.onPrimary,\n    disabledContainerColor = MaterialTheme.colorScheme.primary.copy(alpha = DISABLED_ALPHA),\n    disabledContentColor = MaterialTheme.colorScheme.onPrimary,\n)\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun HomeLargeCardPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) enabled: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            HomeLargeCard(\n                data = HomeCardItem.LargeCard(\n                    title = R.string.quick_quiz,\n                    icon = CardIcon.Icon(Icons.Rounded.Check),\n                    onClick = {},\n                    enabled = enabled\n                ),\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun PrimaryHomeLargeCardPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) enabled: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            HomeLargeCard(\n                data = HomeCardItem.LargeCard(\n                    title = R.string.quick_quiz,\n                    icon = CardIcon.Icon(Icons.Rounded.Check),\n                    onClick = {},\n                    enabled = enabled,\n                    backgroundPrimary = true\n                ),\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/components/HomeMediumCard.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card.components\n\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material3.*\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.LocalInspectionMode\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.home_card.model.CardIcon\nimport com.infinitepower.newquiz.core.ui.home_card.model.HomeCardItem\n\n@Composable\nfun HomeMediumCard(\n    modifier: Modifier = Modifier,\n    data: HomeCardItem.MediumCard\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val title = stringResource(id = data.title)\n\n    val isInspectionMode = LocalInspectionMode.current\n\n    Card(\n        onClick = data.onClick,\n        enabled = data.enabled || isInspectionMode,\n        modifier = modifier\n    ) {\n        Row(\n            modifier = Modifier.padding(spaceMedium),\n            verticalAlignment = Alignment.CenterVertically\n        ) {\n            HomeCardIcon(\n                icon = data.icon,\n                contentDescription = title,\n                modifier = Modifier\n                    .size(75.dp)\n                    .padding(MaterialTheme.spacing.small),\n            )\n            Spacer(modifier = Modifier.width(spaceMedium))\n            Column {\n                Text(\n                    text = title,\n                    style = MaterialTheme.typography.titleMedium,\n                    modifier = Modifier.fillMaxWidth()\n                )\n                if (data.description != null) {\n                    Spacer(modifier = Modifier.height(MaterialTheme.spacing.small))\n                    Text(\n                        text = data.description,\n                        style = MaterialTheme.typography.bodyMedium,\n                        modifier = Modifier.fillMaxWidth()\n                    )\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun HomeMediumCardPreview() {\n    NewQuizTheme {\n        Surface {\n            HomeMediumCard(\n                data = HomeCardItem.MediumCard(\n                    title = R.string.quick_quiz,\n                    description = \"10 Questions\",\n                    icon = CardIcon.Icon(Icons.Rounded.Check),\n                    onClick = {},\n                ),\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/components/PlayRandomQuizCard.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card.components\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Brush\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\n\n@Composable\nfun PlayRandomQuizCard(\n    modifier: Modifier = Modifier,\n    title: String,\n    buttonTitle: String,\n    containerMainColor: Color = MaterialTheme.colorScheme.primary,\n    enabled: Boolean = true,\n    onClick: () -> Unit\n) {\n    val backgroundColor = Brush.horizontalGradient(\n        colors = listOf(\n            containerMainColor,\n            containerMainColor.copy(alpha = 0.8f)\n        )\n    )\n\n    val textColor = if (enabled) {\n        MaterialTheme.colorScheme.surface\n    } else {\n        MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)\n    }\n\n    val cardShape = MaterialTheme.shapes.large\n\n    Box(\n        modifier = modifier.clickable(\n            enabled = enabled,\n            onClick = onClick,\n            role = Role.Button\n        ).background(\n            brush = backgroundColor,\n            shape = cardShape,\n            alpha = if (enabled) 1f else 0.12f\n        ),\n    ) {\n        Column(\n            modifier = Modifier\n                .fillMaxWidth()\n                .padding(\n                    horizontal = 24.dp,\n                    vertical = 24.dp\n                ),\n            verticalArrangement = Arrangement.spacedBy(24.dp)\n        ) {\n            Text(\n                text = title,\n                color = textColor,\n                modifier = Modifier.fillMaxWidth(),\n                style = MaterialTheme.typography.titleMedium\n            )\n            Button(\n                onClick = onClick,\n                colors = ButtonDefaults.buttonColors(\n                    containerColor = MaterialTheme.colorScheme.surface,\n                    contentColor = MaterialTheme.colorScheme.onSurface\n                ),\n                enabled = enabled,\n            ) {\n                Text(text = buttonTitle)\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun MultiChoiceCategoriesPreview() {\n    NewQuizTheme {\n        Surface {\n            PlayRandomQuizCard(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .padding(16.dp),\n                title = \"PLay a quiz with random categories\",\n                buttonTitle = \"Random Quiz\",\n                onClick = {},\n                enabled = false\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/home_card/model/HomeCardItem.kt",
    "content": "package com.infinitepower.newquiz.core.ui.home_card.model\n\nimport androidx.annotation.StringRes\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport com.airbnb.lottie.compose.LottieCompositionSpec\n\nsealed class HomeCardItem {\n    fun getId(): Int = this.hashCode()\n\n    data class GroupTitle(\n        @StringRes val title: Int\n    ) : HomeCardItem()\n\n    data class LargeCard(\n        @StringRes val title: Int,\n        val icon: CardIcon,\n        val enabled: Boolean = true,\n        val backgroundPrimary: Boolean = false,\n        val onClick: () -> Unit\n    ) : HomeCardItem()\n\n    data class MediumCard(\n        @StringRes val title: Int,\n        val description: String? = null,\n        val icon: CardIcon,\n        val enabled: Boolean = true,\n        val onClick: () -> Unit\n    ) : HomeCardItem()\n\n    data class HorizontalItems <out T : Any> (\n        val items: List<T>,\n        val itemContent: @Composable (item: @UnsafeVariance T) -> Unit\n    ) : HomeCardItem()\n\n    data class CustomItem(\n        val content: @Composable () -> Unit\n    ) : HomeCardItem()\n}\n\nsealed class CardIcon {\n    data class Icon(val vector: ImageVector) : CardIcon()\n\n    data class Lottie(val spec: LottieCompositionSpec) : CardIcon()\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/icons/TrophyIcon.kt",
    "content": "package com.infinitepower.newquiz.core.ui.icons\n\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.PathFillType\nimport androidx.compose.ui.graphics.SolidColor\nimport androidx.compose.ui.graphics.StrokeCap\nimport androidx.compose.ui.graphics.StrokeJoin\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport androidx.compose.ui.graphics.vector.path\nimport androidx.compose.ui.unit.dp\n\npublic val Trophy: ImageVector\n    get() {\n        if (_Trophy != null) {\n            return _Trophy!!\n        }\n        _Trophy = ImageVector.Builder(\n            name = \"Trophy\",\n            defaultWidth = 24.dp,\n            defaultHeight = 24.dp,\n            viewportWidth = 960f,\n            viewportHeight = 960f\n        ).apply {\n            path(\n                fill = SolidColor(Color.Black),\n                fillAlpha = 1.0f,\n                stroke = null,\n                strokeAlpha = 1.0f,\n                strokeLineWidth = 1.0f,\n                strokeLineCap = StrokeCap.Butt,\n                strokeLineJoin = StrokeJoin.Miter,\n                strokeLineMiter = 1.0f,\n                pathFillType = PathFillType.NonZero\n            ) {\n                moveTo(440f, 760f)\n                verticalLineToRelative(-124f)\n                quadToRelative(-49f, -11f, -87.5f, -41.5f)\n                reflectiveQuadTo(296f, 518f)\n                quadToRelative(-75f, -9f, -125.5f, -65.5f)\n                reflectiveQuadTo(120f, 320f)\n                verticalLineToRelative(-40f)\n                quadToRelative(0f, -33f, 23.5f, -56.5f)\n                reflectiveQuadTo(200f, 200f)\n                horizontalLineToRelative(80f)\n                quadToRelative(0f, -33f, 23.5f, -56.5f)\n                reflectiveQuadTo(360f, 120f)\n                horizontalLineToRelative(240f)\n                quadToRelative(33f, 0f, 56.5f, 23.5f)\n                reflectiveQuadTo(680f, 200f)\n                horizontalLineToRelative(80f)\n                quadToRelative(33f, 0f, 56.5f, 23.5f)\n                reflectiveQuadTo(840f, 280f)\n                verticalLineToRelative(40f)\n                quadToRelative(0f, 76f, -50.5f, 132.5f)\n                reflectiveQuadTo(664f, 518f)\n                quadToRelative(-18f, 46f, -56.5f, 76.5f)\n                reflectiveQuadTo(520f, 636f)\n                verticalLineToRelative(124f)\n                horizontalLineToRelative(120f)\n                quadToRelative(17f, 0f, 28.5f, 11.5f)\n                reflectiveQuadTo(680f, 800f)\n                reflectiveQuadToRelative(-11.5f, 28.5f)\n                reflectiveQuadTo(640f, 840f)\n                horizontalLineTo(320f)\n                quadToRelative(-17f, 0f, -28.5f, -11.5f)\n                reflectiveQuadTo(280f, 800f)\n                reflectiveQuadToRelative(11.5f, -28.5f)\n                reflectiveQuadTo(320f, 760f)\n                close()\n                moveTo(280f, 432f)\n                verticalLineToRelative(-152f)\n                horizontalLineToRelative(-80f)\n                verticalLineToRelative(40f)\n                quadToRelative(0f, 38f, 22f, 68.5f)\n                reflectiveQuadToRelative(58f, 43.5f)\n                moveToRelative(200f, 128f)\n                quadToRelative(50f, 0f, 85f, -35f)\n                reflectiveQuadToRelative(35f, -85f)\n                verticalLineToRelative(-240f)\n                horizontalLineTo(360f)\n                verticalLineToRelative(240f)\n                quadToRelative(0f, 50f, 35f, 85f)\n                reflectiveQuadToRelative(85f, 35f)\n                moveToRelative(200f, -128f)\n                quadToRelative(36f, -13f, 58f, -43.5f)\n                reflectiveQuadToRelative(22f, -68.5f)\n                verticalLineToRelative(-40f)\n                horizontalLineToRelative(-80f)\n                close()\n                moveToRelative(-200f, -52f)\n            }\n        }.build()\n        return _Trophy!!\n    }\n\nprivate var _Trophy: ImageVector? = null\n\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/ui/text/CompactDecimalText.kt",
    "content": "package com.infinitepower.newquiz.core.ui.text\n\nimport android.icu.text.CompactDecimalFormat\nimport android.os.Build\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.LocalTextStyle\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport java.util.Locale\nimport kotlin.math.ln\nimport kotlin.math.pow\n\n@Composable\nfun CompactDecimalText(\n    modifier: Modifier = Modifier,\n    value: Int,\n    style: TextStyle = LocalTextStyle.current\n) {\n    val text = remember(value) { getCompactDecimalText(value) }\n\n    Text(\n        modifier = modifier,\n        text = text,\n        style = style\n    )\n}\n\nprivate fun getCompactDecimalText(value: Int): String {\n    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n        val formatter = CompactDecimalFormat.getInstance(\n            Locale.getDefault(),\n            CompactDecimalFormat.CompactStyle.SHORT\n        )\n\n        formatter.format(value)\n    } else {\n        value.compactFormat()\n    }\n}\n\nprivate fun Int.compactFormat(): String {\n    if (this < 1000) return toString()\n    val exp = (ln(this.toDouble()) / ln(1000.0)).toInt()\n    return String.format(\"%.1f %c\", this / 1000.0.pow(exp.toDouble()), \"kMGTPE\"[exp - 1])\n}\n\n@Composable\n@PreviewLightDark\nprivate fun CompactDecimalTextPreview() {\n    NewQuizTheme {\n        Surface {\n            CompactDecimalText(\n                modifier = Modifier.padding(16.dp),\n                value = 1234\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/ComposeUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util\n\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.calculateEndPadding\nimport androidx.compose.foundation.layout.calculateStartPadding\nimport androidx.compose.ui.unit.LayoutDirection\n\noperator fun PaddingValues.plus(other: PaddingValues): PaddingValues = PaddingValues(\n    start = this.calculateStartPadding(LayoutDirection.Ltr) +\n        other.calculateStartPadding(LayoutDirection.Ltr),\n    top = this.calculateTopPadding() + other.calculateTopPadding(),\n    end = this.calculateEndPadding(LayoutDirection.Ltr) +\n        other.calculateEndPadding(LayoutDirection.Ltr),\n    bottom = this.calculateBottomPadding() + other.calculateBottomPadding(),\n)\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/PackageUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util\n\nimport android.content.pm.PackageManager\nimport android.os.Build\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.platform.LocalContext\n\n@Composable\nfun rememberAppVersion(): String {\n    val context = LocalContext.current\n\n    return remember {\n        val info = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            context.packageManager.getPackageInfo(context.packageName, PackageManager.PackageInfoFlags.of(0))\n        } else {\n            @Suppress(\"DEPRECATION\")\n            context.packageManager.getPackageInfo(context.packageName, 0)\n        }\n\n        info.versionName ?: \"Unknown\"\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/UiTextUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util\n\nimport android.content.Context\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.ReadOnlyComposable\nimport androidx.compose.ui.res.pluralStringResource\nimport androidx.compose.ui.res.stringResource\nimport com.infinitepower.newquiz.model.UiText\n\n@Composable\n@ReadOnlyComposable\nfun UiText.asString(): String {\n    val argsFormatted = formatArgs(*args)\n\n    return when (this) {\n        is UiText.DynamicString -> value.format(*argsFormatted)\n        is UiText.StringResource -> stringResource(resId, *argsFormatted)\n        is UiText.PluralStringResource -> pluralStringResource(id = resId, count = quantity, *argsFormatted)\n    }\n}\n\nfun UiText.asString(context: Context): String {\n    val argsFormatted = context.formatArgs(*args)\n\n    return when (this) {\n        is UiText.DynamicString -> value.format(*argsFormatted)\n        is UiText.StringResource -> context.getString(resId, *argsFormatted)\n        is UiText.PluralStringResource -> context.resources.getQuantityString(resId, quantity, *argsFormatted)\n    }\n}\n\n@Composable\n@ReadOnlyComposable\nprivate fun formatArgs(\n    vararg args: Any\n): Array<out Any> = args.map { arg ->\n    when (arg) {\n        is UiText -> arg.asString()\n        else -> arg\n    }\n}.toTypedArray()\n\nprivate fun Context.formatArgs(\n    vararg args: Any\n): Array<out Any> = args.map { arg ->\n    when (arg) {\n        is UiText -> arg.asString(this)\n        else -> arg\n    }\n}.toTypedArray()\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/UriUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util\n\nimport android.net.Uri\nimport java.net.URI\n\nfun URI.toAndroidUri(): Uri = Uri.parse(this.toString())\n\nfun Uri.toJavaURI(): URI = URI.create(this.toString())\n\nfun emptyJavaURI(): URI = URI.create(\"\")\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/android/DrawableUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util.android\n\n/*\n/**\n * Get the dominant color of the image asynchronously\n */\nfun Drawable.getDominantColor(\n    onFinish: (Color) -> Unit\n) {\n    val bitmap = (this as BitmapDrawable).bitmap.copy(Bitmap.Config.ARGB_8888, true)\n\n    Palette.from(bitmap).generate { palette ->\n        palette?.vibrantSwatch?.rgb?.let { colorValue ->\n            onFinish(Color(colorValue))\n        }\n    }\n}\n\n */"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/android/resources/ResourcesUtil.kt",
    "content": "package com.infinitepower.newquiz.core.util.android.resources\n\nimport android.content.res.Resources\nimport androidx.annotation.RawRes\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport kotlinx.serialization.json.Json\n\nsuspend inline fun <reified T> Resources.readRawJson(@RawRes rawResId: Int): T {\n    return withContext(Dispatchers.IO) {\n        val resStr = openRawResource(rawResId).use {\n            it.readBytes().decodeToString()\n        }\n\n        Json.decodeFromString(resStr)\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/collections/Collections.kt",
    "content": "package com.infinitepower.newquiz.core.util.collections\n\ninline fun <T> Iterable<T>.indexOfFirstOrNull(predicate: (T) -> Boolean) = indexOfFirst(predicate).takeIf { it >= 0 }"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/kotlin/BooleanUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nfun Boolean.toInt(): Int = if (this) 1 else 0\n\nfun Boolean.toLong(): Long = if (this) 1L else 0L\n\nfun Boolean.toULong(): ULong = if (this) 1uL else 0uL"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/kotlin/CollectionsUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nimport kotlin.random.Random\n\n/**\n * Returns the sum of all elements in the collection.\n */\n@JvmName(\"sumOfIntRange\")\nfun Iterable<IntRange>.sum(): IntRange {\n    if (count() == 0) return 0..0\n\n    return reduce { acc, intRange ->\n        (acc.first + intRange.first)..(acc.last + intRange.last)\n    }\n}\n\ninfix fun ClosedFloatingPointRange<Float>.increaseEndBy(\n    other: Float\n): ClosedFloatingPointRange<Float> {\n    return endInclusive..(endInclusive + other)\n}\n\nsuspend fun <T> generateRandomUniqueItems(\n    itemCount: Int,\n    generator: suspend () -> T,\n    maxIterations: Int = Int.MAX_VALUE,\n    exclusions: List<T> = emptyList()\n): Iterable<T> {\n    val items = HashSet<T>()\n    var iterations = 0\n\n    while (items.size < itemCount && iterations < maxIterations) {\n        val generatedItem = generator() // Generate new item\n        if (generatedItem in exclusions) continue // Checks if generated item is not in items\n        items.add(generatedItem)\n        iterations++\n    }\n\n    return items\n}\n\n/**\n * Generates a list of [answerCount] number of incorrect answers for a given [correctSolution] integer.\n * @param answerCount The number of incorrect answers to generate.\n * @param correctSolution The correct solution to the question.\n * @param fromRange The minimum range of values from which to generate incorrect answers. Default value is 10.\n * @param toRange The maximum range of values from which to generate incorrect answers. Default value is 10.\n * @param random The random number generator used to generate the incorrect answers.\n*/\nsuspend fun generateIncorrectNumberAnswers(\n    answerCount: Int,\n    correctSolution: Int,\n    fromRange: Int = 10,\n    toRange: Int = 10,\n    random: Random = Random\n): List<Int> = generateRandomUniqueItems(\n    itemCount = answerCount,\n    exclusions = listOf(correctSolution),\n    generator = {\n        random.nextInt(correctSolution - fromRange, correctSolution + toRange)\n    }\n).toList()\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/kotlin/Math.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nimport kotlin.math.pow\nimport kotlin.math.roundToInt\n\nfun Double.roundToUInt(): UInt = roundToInt().toUInt()\n\ninfix fun UInt.pow(n: Int): UInt = toDouble().pow(n).toUInt()\n\ninfix operator fun ULong.div(other: Float): Float = toLong() / other\n\ninfix operator fun ULong.div(other: Double): Double = toLong() / other\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/kotlin/NumberUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nfun Int.toDoubleDigit(): String = String.format(\"%02d\", this)\n\noperator fun UInt.times(multiplierFactor: Float): UInt {\n    return this.toInt().times(multiplierFactor).toUInt()\n}\n\noperator fun UIntRange.times(multiplierFactor: Float): UIntRange {\n    return this.first * multiplierFactor..this.last * multiplierFactor\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/kotlin/SetUtils.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nfun <T> Set<T>.removeFirst(): Set<T> {\n    return this - first()\n}\n\nfun <T> Set<T>.removeLast(): Set<T> {\n    return this - last()\n}\n"
  },
  {
    "path": "core/src/main/java/com/infinitepower/newquiz/core/util/model/QuestionDifficultyUtil.kt",
    "content": "package com.infinitepower.newquiz.core.util.model\n\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.core.R as CoreR\n\nfun QuestionDifficulty.getText(): UiText = when (this) {\n    is QuestionDifficulty.Easy -> UiText.StringResource(CoreR.string.easy)\n    is QuestionDifficulty.Medium -> UiText.StringResource(CoreR.string.medium)\n    is QuestionDifficulty.Hard -> UiText.StringResource(CoreR.string.hard)\n}"
  },
  {
    "path": "core/src/main/res/drawable/github_logo.xml",
    "content": "<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\" android:height=\"24dp\"\n    android:viewportWidth=\"28\"\n    android:viewportHeight=\"24\">\n  <path android:fillColor=\"#FF000000\" android:pathData=\"M14 0.3c-6.6 0-12 5.4-12 12c0 5.3 3.4 9.8 8.2 11.4c0.6 0.1 0.8-0.3 0.8-0.6v-2.2c-3.3 0.7-4-1.4-4-1.4c-0.6-1.4-1.4-1.8-1.4-1.8c-1.1-0.7 0.1-0.7 0.1-0.7c1.2 0.1 1.9 1.2 1.9 1.2c1 1.8 2.8 1.3 3.4 1c0.2-0.8 0.5-1.3 0.8-1.6c-2.7-0.3-5.5-1.3-5.5-5.9c0-1.3 0.5-2.4 1.3-3.3c-0.1-0.3-0.6-1.5 0.1-3.1c0 0 1-0.3 3.3 1.2c1-0.3 2-0.4 3-0.4s2 0.1 3 0.4c2.3-1.5 3.3-1.2 3.3-1.2c0.7 1.6 0.2 2.8 0.1 3.1c0.8 0.9 1.3 2 1.3 3.3c0 4.6-2.9 5.6-5.5 5.9c0.4 0.4 0.8 1.1 0.8 2.2v3.3c0 0.3 0.2 0.7 0.8 0.6c4.8-1.6 8.2-6.1 8.2-11.4c0-6.6-5.4-12-12-12z\"/>\n</vector>\n"
  },
  {
    "path": "core/src/main/res/drawable/logo_monochromatic.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"200dp\"\n    android:height=\"200dp\"\n    android:viewportWidth=\"500\"\n    android:viewportHeight=\"500\">\n  <path\n      android:pathData=\"M227,126.1c-5.2,1.1 -13.5,3.4 -18.5,5.1 -49.3,16.8 -83.5,65.3 -83.5,118.3 0,24.9 8.4,51.2 23.1,71.7 23.5,33 62.5,53.4 101.9,53.2 23.8,-0.1 47.6,-7.4 68.9,-21.1 10.5,-6.8 27.8,-23.8 34.5,-34.1 22.7,-34.5 27.6,-75 13.9,-112.7 -2.6,-7 -8.8,-19.7 -12.1,-24.6 -0.9,-1.4 -3,-4.5 -4.6,-6.9 -3.8,-5.8 -19.6,-21.6 -26.9,-26.9 -10.2,-7.4 -28.9,-16.6 -39.2,-19.2 -2.2,-0.6 -6.7,-1.7 -10,-2.5 -3.4,-0.8 -12.9,-1.7 -22,-1.9 -13.5,-0.4 -17.5,-0.2 -25.5,1.6zM269.2,163.5c33.9,7.8 60.5,35.4 66.4,69 4.4,24.9 -1.6,51.2 -15.6,69.1 -15.9,20.1 -36.7,31.9 -60.6,34.4 -39.7,4.1 -76.8,-18.5 -90.8,-55.5 -4.6,-11.9 -5.7,-18 -5.7,-31.2 -0.1,-24.7 8.7,-45.8 25.8,-62.4 12.7,-12.3 23.6,-18.5 40.3,-23 8.5,-2.3 31.2,-2.5 40.2,-0.4z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M247.1,202.8c-1.5,5.3 -0.1,9.7 3,9.7 2.1,-0 2.4,-0.5 2.7,-4.4 0.5,-6.2 -0.4,-9.1 -2.8,-9.1 -1.4,-0 -2.2,1.1 -2.9,3.8z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M214.7,213.7c-1.5,1.4 -0.5,4.1 2.6,7.1 4,3.9 7.7,4.3 7.7,0.9 0,-4.1 -8,-10.4 -10.3,-8z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M277.6,217c-4.1,4.6 -4.2,6.3 -0.4,6.8 1.8,0.2 3.3,-0.6 5.7,-3.3 3.5,-4.1 3.7,-6.8 0.5,-7.3 -1.4,-0.2 -3.3,1.1 -5.8,3.8z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M239.8,221.9c-7.5,3 -12.4,7.5 -16,14.8 -5.8,11.9 -3.9,23 5.9,33.3 2.4,2.5 5.1,5.7 5.9,7.2l1.5,2.8 12.7,-0 12.8,-0 1.7,-2.8c0.9,-1.5 3.5,-4.7 5.8,-7.2 2.3,-2.5 5.3,-6.9 6.6,-9.9 5,-10.6 1.4,-25.9 -7.9,-33.3 -8.4,-6.6 -19.7,-8.5 -29,-4.9zM258.7,232.6c6.2,3 11.6,13.2 8.9,16.9 -1.8,2.5 -4.2,0.9 -5.5,-3.7 -1.4,-4.7 -5.4,-8.8 -8.6,-8.8 -3.1,-0 -5.9,-2.4 -5.2,-4.4 0.8,-2.1 5.8,-2.1 10.4,-0z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M201.2,246.9c-0.8,0.5 -1.2,1.7 -1,2.7 0.3,1.6 1.3,1.9 6.8,1.9 5.5,-0 6.5,-0.3 6.8,-1.9 0.5,-2.5 -1.4,-3.6 -6.8,-3.6 -2.5,-0 -5.1,0.4 -5.8,0.9z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M286,247.5c-1,1.2 -1,1.8 0,3 1,1.2 2.9,1.5 7.4,1.3 5,-0.2 6.1,-0.6 6.4,-2.2 0.5,-2.6 -1.5,-3.6 -7.4,-3.6 -3.4,-0 -5.6,0.5 -6.4,1.5z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M218,276.6c-4.4,4 -5.1,7.7 -1.5,8.2 2.9,0.4 8.8,-5.9 8.3,-8.9 -0.5,-3.5 -2.3,-3.3 -6.8,0.7z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M275.6,274.5c-1.9,1.9 -1.1,4.1 2.7,7.8 3.1,3 7,3.3 7.5,0.6 0.3,-1.8 -4,-7.4 -6.8,-8.9 -1.4,-0.7 -2.3,-0.6 -3.4,0.5z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M237.8,287.5c-1.1,1.1 1,8.7 2.8,10 1.3,1 4.5,1.4 9.7,1.5 9.3,-0 11.7,-1.6 11.7,-8l0,-4 -11.8,-0c-6.5,-0 -12.1,0.2 -12.4,0.5z\"\n      android:fillColor=\"#000000\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "core/src/main/res/drawable/round_android_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M17.6,9.48l1.84,-3.18c0.16,-0.31 0.04,-0.69 -0.26,-0.85c-0.29,-0.15 -0.65,-0.06 -0.83,0.22l-1.88,3.24c-2.86,-1.21 -6.08,-1.21 -8.94,0L5.65,5.67c-0.19,-0.29 -0.58,-0.38 -0.87,-0.2C4.5,5.65 4.41,6.01 4.56,6.3L6.4,9.48C3.3,11.25 1.28,14.44 1,18h22C22.72,14.44 20.7,11.25 17.6,9.48zM7,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25S8.25,13.31 8.25,14C8.25,14.69 7.69,15.25 7,15.25zM17,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25s1.25,0.56 1.25,1.25C18.25,14.69 17.69,15.25 17,15.25z\"/>\n</vector>\n"
  },
  {
    "path": "core/src/main/res/drawable/round_flag_circle_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10s10,-4.48 10,-10C22,6.48 17.52,2 12,2zM17,15h-3.38c-0.38,0 -0.73,-0.21 -0.89,-0.55L12,13H9.5v4.25C9.5,17.66 9.16,18 8.75,18h0C8.34,18 8,17.66 8,17.25V8c0,-0.55 0.45,-1 1,-1h4.38c0.38,0 0.73,0.21 0.89,0.55L15,9h2c0.55,0 1,0.45 1,1v4C18,14.55 17.55,15 17,15z\"/>\n</vector>\n"
  },
  {
    "path": "core/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n    <monochrome android:drawable=\"@drawable/logo_monochromatic\" />\n</adaptive-icon>"
  },
  {
    "path": "core/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n    <monochrome android:drawable=\"@drawable/logo_monochromatic\" />\n</adaptive-icon>"
  },
  {
    "path": "core/src/main/res/raw/trophy2.json",
    "content": "{\"v\":\"5.8.1\",\"fr\":30,\"ip\":0,\"op\":71,\"w\":500,\"h\":500,\"nm\":\"Trophy\",\"ddd\":0,\"assets\":[{\"id\":\"comp_0\",\"nm\":\"Pre-comp 3\",\"fr\":30,\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":0,\"nm\":\"Pre-comp 2\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[391.176,345.588,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[50,48.5,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[30,30,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":100,\"h\":97,\"ip\":2,\"op\":17,\"st\":2,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":0,\"nm\":\"Pre-comp 2\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[344.118,294.118,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[50,48.5,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":100,\"h\":97,\"ip\":1,\"op\":16,\"st\":1,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":0,\"nm\":\"Pre-comp 2\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[151.471,317.647,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[50,48.5,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[30,30,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":100,\"h\":97,\"ip\":7,\"op\":22,\"st\":7,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":0,\"nm\":\"Pre-comp 2\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[104.412,266.176,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[50,48.5,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":100,\"h\":97,\"ip\":6,\"op\":21,\"st\":6,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":0,\"nm\":\"Pre-comp 2\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[342.647,145.588,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[50,48.5,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[30,30,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":100,\"h\":97,\"ip\":4,\"op\":19,\"st\":4,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":0,\"nm\":\"Pre-comp 2\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[295.588,94.118,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[50,48.5,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":100,\"h\":97,\"ip\":3,\"op\":18,\"st\":3,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":0,\"nm\":\"Pre-comp 2\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[133.824,122.059,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[50,48.5,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[30,30,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":100,\"h\":97,\"ip\":1,\"op\":16,\"st\":1,\"bm\":0},{\"ddd\":0,\"ind\":8,\"ty\":0,\"nm\":\"Pre-comp 2\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[179.412,82.353,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[50,48.5,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":100,\"h\":97,\"ip\":0,\"op\":15,\"st\":0,\"bm\":0}]},{\"id\":\"comp_1\",\"nm\":\"Pre-comp 2\",\"fr\":30,\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"Shape Layer 12\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":-90,\"ix\":10},\"p\":{\"a\":0,\"k\":[50.5,47,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[-142.5,-154,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-142.5,-154],[-101.5,-154]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":4,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[0]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[0]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":15,\"st\":-11,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"Shape Layer 11\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":180,\"ix\":10},\"p\":{\"a\":0,\"k\":[50.5,47,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[-142.5,-154,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-142.5,-154],[-101.5,-154]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":4,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[0]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[0]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":15,\"st\":-11,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"Shape Layer 10\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":90,\"ix\":10},\"p\":{\"a\":0,\"k\":[50.5,47,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[-142.5,-154,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-142.5,-154],[-101.5,-154]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":4,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[0]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[0]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":15,\"st\":-11,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"Shape Layer 9\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[50.5,47,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[-142.5,-154,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-142.5,-154],[-101.5,-154]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":4,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[0]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[0]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":15,\"st\":-11,\"bm\":0}]},{\"id\":\"comp_2\",\"nm\":\"Pre-comp 1\",\"fr\":30,\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"Shape Layer 10\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"Shape Layer 11\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":30,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"Shape Layer 12\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":60,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"Shape Layer 13\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":90,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":4,\"nm\":\"Shape Layer 14\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":120,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"Shape Layer 15\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":150,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":4,\"nm\":\"Shape Layer 16\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":180,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":8,\"ty\":4,\"nm\":\"Shape Layer 17\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":210,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":9,\"ty\":4,\"nm\":\"Shape Layer 18\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":240,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":10,\"ty\":4,\"nm\":\"Shape Layer 19\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":270,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":4,\"s\":[60]},{\"t\":14,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":11,\"ty\":4,\"nm\":\"Shape Layer 21\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":300,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":3,\"s\":[60]},{\"t\":13,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":12,\"ty\":4,\"nm\":\"Shape Layer 20\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":330,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[0,0],[178,0]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.705882352941,0.247058838489,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tm\",\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":3,\"s\":[60]},{\"t\":13,\"s\":[100]}],\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"t\":0,\"s\":[60]},{\"t\":10,\"s\":[100]}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\",\"hd\":false}],\"ip\":0,\"op\":300,\"st\":0,\"bm\":0}]}],\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":0,\"nm\":\"Pre-comp 3\",\"refId\":\"comp_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[250,250,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[-100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":500,\"h\":500,\"ip\":39,\"op\":61,\"st\":39,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":0,\"nm\":\"Pre-comp 3\",\"refId\":\"comp_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[250,250,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":500,\"h\":500,\"ip\":24,\"op\":46,\"st\":24,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"Cup 3\",\"parent\":14,\"td\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0.371,-98.838,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.8,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[{\"i\":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],\"o\":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],\"v\":[[-49.8,-128.285],[49.3,-128.285],[70.626,-106.958],[62.096,-21.652],[-62.596,-21.652],[-71.126,-106.958]],\"c\":true}]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.2,\"y\":0},\"t\":14,\"s\":[{\"i\":[[0,6.785],[0,0],[0,-11.667],[0,0],[0,55.777],[0,0]],\"o\":[[0,0],[0,8.035],[0,0],[0,54.652],[0,0],[0,-12.042]],\"v\":[[-0.25,-128.285],[-0.25,-128.285],[-0.25,-106.958],[-0.25,-21.652],[-0.25,-21.652],[-0.25,-106.958]],\"c\":true}]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":18,\"s\":[{\"i\":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],\"o\":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],\"v\":[[-49.8,-128.285],[49.3,-128.285],[70.626,-106.958],[62.096,-21.652],[-62.596,-21.652],[-71.126,-106.958]],\"c\":true}]},{\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":24,\"s\":[{\"i\":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],\"o\":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],\"v\":[[-49.8,-128.285],[49.3,-128.285],[70.626,-106.958],[62.096,-21.652],[-62.596,-21.652],[-71.126,-106.958]],\"c\":true}]},{\"i\":{\"x\":0.223,\"y\":1},\"o\":{\"x\":0.2,\"y\":0},\"t\":31,\"s\":[{\"i\":[[0,6.785],[0,0],[0,-11.667],[0,0],[0,55.777],[0,0]],\"o\":[[0,0],[0,8.035],[0,0],[0,54.652],[0,0],[0,-12.042]],\"v\":[[-0.25,-128.285],[-0.25,-128.285],[-0.25,-106.958],[-0.25,-21.652],[-0.25,-21.652],[-0.25,-106.958]],\"c\":true}]},{\"t\":50,\"s\":[{\"i\":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],\"o\":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],\"v\":[[-49.8,-128.285],[49.3,-128.285],[70.626,-106.958],[62.096,-21.652],[-62.596,-21.652],[-71.126,-106.958]],\"c\":true}]}],\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.705882370472,0.247058823705,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Cup\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":31,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"Shape Layer 7\",\"tt\":2,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":1,\"k\":[{\"t\":24,\"s\":[{\"i\":[[3.191,-0.395],[2.304,-0.927],[2.095,-1.709],[1.788,-1.877],[0.908,-1.912],[-1.334,-6.312],[-2.779,-4.188],[-3.401,-3.602],[-3.548,-3.297],[-2.312,-2.352],[-2.506,-2.506],[-2.476,-2.535],[-0.232,-1.997],[0.723,-0.831],[0.267,-1.304],[-2.88,-0.857],[1.3,9.712],[4.203,4.76],[9.453,16.328],[-0.295,3.28],[-3.343,1.249],[-4.023,-0.951],[-1.8,-0.768],[-8.286,2.069],[-0.398,3.182],[3.129,3.445],[1.614,1.176],[1.189,0.657],[2.306,0.956],[2.086,0.582]],\"o\":[[-4.689,0.581],[-2.304,0.927],[-1.938,1.582],[-1.788,1.877],[-3.116,6.566],[1.334,6.312],[2.849,4.294],[3.401,3.602],[2.244,2.084],[2.312,2.352],[2.864,2.864],[2.476,2.535],[0.16,1.372],[-0.723,0.831],[-1.339,6.557],[13.183,3.921],[-1.018,-7.607],[-12.335,-13.97],[-1.509,-2.606],[0.413,-4.602],[3.955,-1.477],[2.275,0.538],[8.878,3.789],[3.458,-0.863],[0.467,-3.729],[-1.703,-1.875],[-1.654,-1.205],[-1.861,-1.028],[-2.371,-0.983],[-7.691,-2.147]],\"v\":[[-93,-111],[-102.945,-108.846],[-109,-105],[-114.773,-99.748],[-119,-94],[-120.921,-74.216],[-114,-58],[-104.525,-46.252],[-94,-36],[-87.197,-29.316],[-80,-22],[-71.526,-13.849],[-67,-7],[-68.18,-3.949],[-70,-1],[-64,12],[-47,-11],[-59,-30],[-99,-72],[-102,-81],[-94,-91],[-82,-91],[-75,-89],[-55,-79],[-48,-87],[-53,-98],[-58,-101],[-62,-105],[-69,-107],[-75,-110]],\"c\":true}],\"h\":1},{\"t\":25,\"s\":[{\"i\":[[6.363,-1.468],[2.979,-2.095],[1.84,-2.639],[0.408,-1.067],[0.408,-1.35],[0.465,-0.387],[0.082,-0.263],[-3.965,-7.542],[-4.029,-4.555],[-0.766,-0.479],[-0.438,-0.523],[-0.104,-0.568],[-0.27,-0.353],[-0.859,-0.529],[-0.842,-0.709],[-4.878,-5.799],[-0.092,-0.71],[0.419,-2.677],[-6.464,-0.238],[-1.53,2.112],[6.189,7.171],[5.82,5.82],[5.515,7.127],[-5.296,5.528],[-9.204,-2.345],[-3.834,-2.514],[-5.231,0.751],[-0.822,3.258],[5.25,2.566],[1.551,0.608]],\"o\":[[-4.243,0.979],[-2.98,2.096],[-0.962,1.38],[-0.408,1.067],[-0.057,0.189],[-0.465,0.387],[-2.889,9.276],[3.965,7.542],[0.497,0.561],[0.766,0.479],[0.313,0.374],[0.104,0.568],[1.053,1.378],[1.068,0.657],[6.494,5.469],[1.271,1.511],[0.356,2.738],[-1.191,7.598],[4.588,0.169],[8.605,-11.877],[-4.677,-5.419],[-6.151,-6.151],[-4.119,-5.322],[4.622,-4.825],[3.701,0.943],[4.525,2.967],[3.142,-0.451],[2.691,-10.661],[-1.826,-0.892],[-7.754,-3.037]],\"v\":[[-95,-111],[-105.802,-106.245],[-113,-99],[-114.915,-95.478],[-116,-92],[-116.981,-91.056],[-118,-90],[-114.689,-64.459],[-101,-46],[-98.956,-44.471],[-97,-43],[-96.468,-41.485],[-96,-40],[-92,-37],[-90,-35],[-71,-17],[-65,-8],[-68,-1],[-59,12],[-49,6],[-55,-29],[-72,-45],[-91,-66],[-97,-87],[-77,-91],[-66,-85],[-54,-79],[-46,-86],[-62,-106],[-67,-109]],\"c\":true}],\"h\":1},{\"t\":26,\"s\":[{\"i\":[[1.111,-0.113],[2.585,-1.009],[1.674,-1.559],[0.573,-0.084],[0.356,-0.336],[0.475,-0.926],[0.564,-0.79],[0.36,-0.281],[0.285,-0.493],[0.871,-2.718],[0.063,-2.618],[-3.733,-5.588],[-2.015,-2.521],[-0.872,-1.07],[-1.274,-1.411],[-7.648,-7.648],[-0.543,-5.007],[0.55,-2.542],[-2.362,-2.153],[-1.562,7.645],[2.913,4.566],[2.86,3.478],[9.12,11.123],[-1.11,7.282],[-2.157,0.726],[-4.835,-3.467],[-4.962,0.362],[-0.24,6.416],[7.573,3.176],[2.407,0.544]],\"o\":[[-3.987,0.405],[-2.585,1.009],[-0.352,0.328],[-0.573,0.084],[-0.536,0.507],[-0.475,0.926],[-0.273,0.382],[-0.36,0.281],[-1.452,2.508],[-0.871,2.718],[-0.253,10.508],[1.754,2.625],[0.961,1.203],[1.009,1.238],[6.895,7.635],[5.268,5.268],[0.253,2.337],[-0.918,4.241],[8.175,7.452],[1.806,-8.842],[-2.806,-4.398],[-8.695,-10.574],[-5.23,-6.378],[0.863,-5.666],[7.845,-2.641],[3.765,2.699],[4.671,-0.341],[0.327,-8.737],[-3.3,-1.384],[-5.338,-1.207]],\"v\":[[-85,-112],[-94.735,-109.865],[-101,-106],[-102.498,-105.506],[-104,-105],[-105.479,-102.712],[-107,-100],[-107.991,-99.083],[-109,-98],[-112.542,-90.083],[-114,-82],[-106,-58],[-100,-51],[-98,-47],[-94,-44],[-75,-23],[-63,-8],[-65,-1],[-63,9],[-43,-1],[-48,-23],[-58,-35],[-84,-62],[-94,-83],[-86,-92],[-64,-86],[-52,-79],[-43,-89],[-63,-108],[-72,-112]],\"c\":true}],\"h\":1},{\"t\":27,\"s\":[{\"i\":[[2.317,-0.535],[3.86,-4.04],[1.242,-4.613],[-2.12,-5.938],[-2.373,-3.37],[-0.492,-0.639],[-0.459,-0.632],[-0.8,-1.421],[-0.923,-1.114],[-0.951,-0.655],[-0.472,-0.507],[-0.081,-0.566],[-0.34,-0.374],[-1.019,-0.973],[-0.936,-1.02],[-0.997,-1.18],[-0.954,-1.127],[-0.458,-4.534],[0.591,-1.92],[-7.875,-0.144],[-0.943,9.517],[4.79,6.294],[7.791,10.593],[-2.16,8.125],[-1.994,0.531],[-4.781,-3.242],[-0.608,-0.61],[-0.949,-0.628],[-0.752,8.522],[12.133,3.114]],\"o\":[[-5.317,1.227],[-3.86,4.04],[-1.851,6.879],[2.12,5.938],[0.571,0.811],[0.492,0.639],[0.875,1.203],[0.8,1.421],[0.723,0.873],[0.951,0.655],[0.337,0.361],[0.081,0.566],[0.99,1.09],[1.018,0.973],[1.058,1.152],[0.997,1.18],[3.481,4.111],[0.271,2.68],[-2.255,7.329],[7.212,0.132],[1.112,-11.222],[-8.19,-10.762],[-4.476,-6.085],[0.814,-3.063],[5.149,-1.372],[0.641,0.434],[1.172,1.175],[7.025,4.649],[0.85,-9.635],[-5.404,-1.387]],\"v\":[[-82,-111],[-96.057,-102.54],[-104,-89],[-102.668,-69.368],[-95,-55],[-93.416,-52.866],[-92,-51],[-89.536,-46.934],[-87,-43],[-84.312,-40.725],[-82,-39],[-81.502,-37.509],[-81,-36],[-77.959,-32.947],[-75,-30],[-71.923,-26.481],[-69,-23],[-58,-8],[-60,-2],[-51,12],[-38,-5],[-48,-30],[-76,-62],[-84,-85],[-77,-92],[-60,-87],[-57,-85],[-55,-81],[-38,-88],[-65,-111]],\"c\":true}],\"h\":1},{\"t\":28,\"s\":[{\"i\":[[9.692,-1.405],[1.755,-0.653],[1.212,-0.989],[0.612,-0.799],[1.064,-1.588],[0.956,-1.834],[0.829,-2.651],[-1.471,-5.599],[-2.124,-4.18],[-2.534,-3.99],[-2.617,-3.613],[-1.064,-1.14],[-0.856,-1.216],[-1.667,-2.958],[-0.212,-2.155],[0.684,-2.633],[-1.441,-2.449],[-1.846,-1.035],[-3.412,0.49],[-1.764,4.555],[0.994,6.213],[1.271,2.573],[1.579,2.614],[5.214,6.995],[2.346,5.732],[-9.454,1.216],[-1.712,-1.097],[-10.861,3.613],[-0.352,2.926],[4.889,4.64]],\"o\":[[-2.767,0.401],[-1.755,0.653],[-1.489,1.215],[-0.612,0.799],[-1.348,2.013],[-0.956,1.834],[-2.008,6.423],[1.471,5.599],[2.249,4.426],[2.534,3.99],[0.888,1.226],[1.064,1.14],[2.12,3.012],[1.667,2.958],[0.297,3.021],[-0.684,2.633],[0.206,0.35],[1.846,1.035],[3.939,-0.566],[1.764,-4.555],[-0.335,-2.095],[-1.271,-2.573],[-3.799,-6.286],[-5.214,-6.995],[-3.248,-7.936],[1.752,-0.225],[5.75,3.685],[2.926,-0.973],[0.526,-4.371],[-6.865,-6.516]],\"v\":[[-63,-111],[-69.666,-109.441],[-74,-107],[-76.82,-104.28],[-79,-101],[-82.389,-95.478],[-85,-89],[-85.099,-70.817],[-79,-56],[-71.776,-43.39],[-64,-32],[-60.976,-28.493],[-58,-25],[-52.068,-15.857],[-49,-8],[-50.358,0.429],[-50,8],[-46.904,10.63],[-39,12],[-30.301,3.736],[-29,-13],[-31.567,-20.11],[-36,-28],[-50.589,-48.416],[-63,-68],[-59,-92],[-53,-89],[-34,-79],[-28,-87],[-36,-101]],\"c\":true}],\"h\":1},{\"t\":29,\"s\":[{\"i\":[[-2.683,7.317],[-0.869,-1.25],[-0.8,-0.658],[-1.031,-0.204],[-1.563,0.111],[-1.735,1.264],[-0.45,1.828],[3.728,4.807],[2.323,0.76],[3.865,-2.557],[1.585,-3.719],[-0.715,-8.537],[-2.363,-6.29],[-1.877,-4.04],[-1.844,-4.454],[-1.186,-3.102],[-0.134,-2.887],[0.496,-1.449],[0.053,-1.394],[-1.552,-2.05],[-2.93,-0.213],[-1.523,0.346],[-0.883,0.689],[-0.327,1.358],[-0.442,1.593],[1.215,6.113],[1.85,4.387],[0.684,1.501],[0.533,1.264],[2.41,7.866]],\"o\":[[1.239,1.98],[0.869,1.25],[0.8,0.658],[1.031,0.204],[1.61,-0.114],[1.735,-1.264],[1.234,-5.011],[-3.728,-4.807],[-5.956,-1.948],[-3.865,2.557],[-3.224,7.564],[0.715,8.537],[1.648,4.387],[1.877,4.04],[1.12,2.706],[1.186,3.102],[0.038,0.811],[-0.496,1.449],[-0.136,3.585],[1.552,2.05],[1.025,0.075],[1.523,-0.346],[1.249,-0.974],[0.327,-1.358],[1.71,-6.16],[-1.215,-6.113],[-0.73,-1.733],[-0.684,-1.501],[-3.044,-7.221],[-2.41,-7.866]],\"v\":[[-33,-87],[-29.914,-82.19],[-27.486,-79.364],[-24.816,-78.105],[-21,-78],[-15.63,-80.215],[-12,-85],[-17.332,-100.689],[-28,-110],[-42.778,-108.25],[-51,-98],[-54.191,-73.044],[-49,-50],[-43.647,-37.55],[-38,-25],[-34.26,-16.136],[-32,-7],[-32.932,-3.437],[-34,1],[-31.799,9.529],[-25,13],[-20.893,12.572],[-17,11],[-14.895,7.464],[-14,3],[-13.83,-15.829],[-19,-32],[-21.148,-36.851],[-23,-41],[-32.295,-63.928]],\"c\":true}],\"h\":1},{\"t\":30,\"s\":[{\"i\":[[3.333,-0.976],[1.133,-1.074],[0.545,-1.572],[0.264,-2.087],[0.291,-2.619],[-0.238,-6.751],[-0.732,-6.624],[-0.753,-6.186],[-0.301,-5.438],[0.011,-2.415],[-0.042,-2.238],[-0.282,-1.704],[-0.709,-0.814],[-1.027,-0.436],[-1.371,-0.226],[-1.409,0.198],[-1.139,0.835],[0.122,7.953],[0.958,8.662],[0.318,3.74],[0.375,3.844],[0.571,4.011],[-0.7,2.69],[-0.723,0.668],[-0.748,0.937],[-0.514,0.926],[-0.22,1.386],[0.663,2.516],[0.717,1.6],[2.195,1.193]],\"o\":[[-2.029,0.594],[-1.133,1.074],[-0.545,1.572],[-0.264,2.087],[-0.729,6.568],[0.238,6.751],[0.732,6.624],[0.753,6.186],[0.124,2.236],[-0.011,2.415],[0.042,2.238],[0.282,1.704],[0.377,0.433],[1.027,0.436],[1.371,0.226],[1.409,-0.198],[3.447,-2.528],[-0.122,-7.953],[-0.353,-3.197],[-0.318,-3.74],[-0.416,-4.266],[-0.571,-4.011],[0.361,-1.39],[0.723,-0.668],[0.726,-0.91],[0.514,-0.926],[0.37,-2.338],[-0.663,-2.516],[-1.885,-4.206],[-2.195,-1.193]],\"v\":[[-11,-109],[-15.667,-106.502],[-18.108,-102.537],[-19.245,-97.054],[-20,-90],[-20.619,-69.944],[-19.046,-49.805],[-16.7,-30.513],[-15,-13],[-14.877,-5.936],[-14.877,1.132],[-14.438,7.133],[-13,11],[-10.818,12.357],[-7.145,13.403],[-2.899,13.497],[1,12],[5.304,-4.9],[3,-31],[2.016,-41.514],[1,-53],[-0.837,-65.682],[-1,-76],[0.711,-78.84],[3,-81],[4.88,-83.643],[6,-87],[5.316,-94.554],[3,-101],[-2.914,-108.886]],\"c\":true}],\"h\":1},{\"t\":31,\"s\":[{\"i\":[[3.441,-0.647],[1.283,-0.953],[0.833,-1.422],[0.508,-1.758],[0.31,-1.96],[-0.087,-1.457],[-0.408,-1.039],[-0.617,-0.938],[-0.714,-1.153],[-0.141,-1.047],[0.131,-1.095],[0.206,-1.125],[0.086,-1.137],[0.415,-3.836],[0.451,-3.58],[0.436,-3.579],[0.369,-3.834],[0.163,-4.005],[-0.565,-3.399],[-1.82,-1.944],[-3.604,0.359],[-1.357,1.89],[-0.311,2.89],[0.076,3.305],[-0.195,3.135],[-0.864,6.178],[-0.881,7.07],[-0.327,7.107],[0.798,6.288],[2.455,2.453]],\"o\":[[-1.859,0.35],[-1.283,0.953],[-0.833,1.422],[-0.508,1.758],[-0.346,2.191],[0.087,1.457],[0.408,1.039],[0.617,0.938],[0.608,0.981],[0.141,1.047],[-0.131,1.095],[-0.206,1.125],[-0.328,4.346],[-0.415,3.836],[-0.451,3.58],[-0.436,3.579],[-0.363,3.764],[-0.163,4.005],[0.565,3.399],[1.82,1.944],[3.06,-0.305],[1.357,-1.89],[0.311,-2.89],[-0.076,-3.305],[0.275,-4.432],[0.864,-6.178],[0.881,-7.07],[0.327,-7.107],[-0.741,-5.836],[-2.455,-2.453]],\"v\":[[11,-109],[6.318,-107.013],[3.176,-103.416],[1.196,-98.612],[0,-93],[-0.361,-87.608],[0.409,-83.943],[1.975,-81.057],[4,-78],[5.074,-74.953],[5.041,-71.735],[4.487,-68.399],[4,-65],[2.874,-52.791],[1.563,-41.731],[0.22,-31.056],[-1,-20],[-1.921,-8.134],[-1.45,3.184],[1.995,11.41],[10,14],[16.461,10.561],[18.798,3.245],[18.986,-6.194],[19,-16],[20.851,-32.129],[23.61,-52.216],[25.564,-73.694],[25,-94],[20.025,-106.362]],\"c\":true}],\"h\":1},{\"t\":32,\"s\":[{\"i\":[[10.012,-0.857],[0.663,-0.066],[0.696,-0.133],[0.685,-0.25],[0.629,-0.419],[1.161,-1.785],[0.997,-2.24],[0.588,-2.086],[-0.068,-1.321],[-1.507,-1.219],[-0.877,-0.316],[-1.658,-0.093],[-0.11,-0.173],[0.768,-3.552],[0.803,-3.414],[1.144,-4.168],[1.192,-4.1],[1.234,-4.969],[-0.056,-4.277],[-2.293,-3.72],[-5.767,1.698],[-1.257,0.847],[-0.419,0.742],[0.306,2.867],[-0.16,2.647],[-0.757,2.689],[-0.84,3.026],[-1.59,5.162],[-1.1,4.811],[1.605,11.555]],\"o\":[[-0.585,0.05],[-0.663,0.066],[-0.696,0.133],[-0.685,0.25],[-1.079,0.718],[-1.161,1.785],[-0.997,2.24],[-0.588,2.086],[0.093,1.806],[1.507,1.219],[1.258,0.453],[1.658,0.093],[0.876,1.379],[-0.768,3.552],[-1.23,5.23],[-1.144,4.168],[-1.119,3.847],[-1.234,4.969],[0.062,4.817],[2.293,3.72],[-0.445,0.131],[1.257,-0.847],[1.063,-1.884],[-0.306,-2.867],[0.134,-2.219],[0.757,-2.689],[1.538,-5.541],[1.59,-5.162],[2.433,-10.639],[-1.605,-11.555]],\"v\":[[27,-109],[25.116,-108.839],[23.066,-108.553],[20.982,-107.991],[19,-107],[15.579,-103.093],[12.28,-96.903],[9.841,-90.262],[9,-85],[11.912,-80.383],[16,-78],[20.861,-77.29],[24,-77],[23.76,-69.026],[21,-58],[17.471,-44.153],[14,-32],[10.119,-18.323],[8,-4],[11.222,9.887],[23,14],[24.852,12.655],[28,10],[28.678,2.572],[28,-6],[29.471,-13.395],[32,-22],[36.828,-38.047],[41,-53],[43.334,-89.622]],\"c\":true}],\"h\":1},{\"t\":33,\"s\":[{\"i\":[[-1.692,-0.766],[1.471,-6.219],[2.553,-5.943],[1.166,-2.647],[1.155,-2.664],[1.283,-2.686],[0.627,-2.395],[-0.895,-5.6],[-2.8,-1.331],[-2.452,0.856],[-0.736,1.221],[0.438,2.954],[-0.248,2.689],[-1.182,2.599],[-0.96,2.214],[-0.473,1.359],[-0.575,1.331],[-0.379,0.478],[-0.227,0.489],[-0.098,0.91],[-0.285,0.66],[-1.26,2.459],[-0.88,2.66],[-0.699,3.206],[-0.248,3.282],[3.214,7.139],[7.793,-0.124],[1.212,-0.836],[-16.354,-1.393],[-1.724,3.235]],\"o\":[[1.434,6.183],[-1.471,6.218],[-1.18,2.747],[-1.166,2.647],[-1.24,2.86],[-1.283,2.686],[-1.379,5.271],[0.895,5.6],[3.456,1.643],[2.452,-0.856],[1.506,-2.497],[-0.438,-2.954],[0.312,-3.39],[1.182,-2.599],[0.535,-1.233],[0.473,-1.359],[0.258,-0.599],[0.379,-0.478],[0.325,-0.701],[0.098,-0.91],[1.079,-2.504],[1.26,-2.459],[0.958,-2.895],[0.698,-3.206],[0.647,-8.57],[-3.214,-7.139],[-2.471,0.04],[-7.887,5.438],[7.4,0.63],[0.183,-0.343]],\"v\":[[38,-85],[37.49,-66.32],[31,-48],[27.481,-39.938],[24,-32],[20.04,-23.651],[17,-16],[16.366,1.455],[22,13],[31.04,13.648],[36,10],[36.944,1.644],[36,-7],[38.514,-15.882],[42,-23],[43.47,-26.927],[45,-31],[46.023,-32.582],[47,-34],[47.53,-36.53],[48,-39],[51.649,-46.383],[55,-54],[57.532,-63.209],[59,-73],[55.33,-98.02],[39,-110],[28,-106],[26,-77],[36,-83]],\"c\":true}],\"h\":1},{\"t\":34,\"s\":[{\"i\":[[14.095,-1.272],[0.937,-0.322],[0.989,-0.507],[0.677,-0.062],[0.614,-0.406],[1.107,-1.421],[0.922,-1.224],[0.408,-0.14],[0.201,-0.216],[0.756,-2.55],[-0.329,-1.4],[-2.06,-1.454],[-3.378,0.441],[-2.23,3.005],[-1.843,-1.444],[-0.405,-1.932],[-0.025,-1.995],[0.392,-1.914],[0.456,-1.6],[2.132,-3.599],[2.472,-4.571],[0.616,-1.544],[0.771,-1.406],[0.656,-1.147],[0.679,-1.473],[-14.013,-1.683],[-0.906,4.713],[-0.286,3.517],[-3.246,5.959],[-1.098,19.932]],\"o\":[[-2.264,0.204],[-0.937,0.322],[-0.678,0.347],[-0.677,0.062],[-1.651,1.092],[-1.107,1.421],[-0.169,0.224],[-0.407,0.14],[-1.305,1.401],[-0.756,2.55],[0.382,1.624],[2.06,1.454],[3.235,-0.423],[2.23,-3.005],[0.353,0.276],[0.405,1.932],[0.022,1.745],[-0.392,1.914],[-2.002,7.031],[-2.132,3.6],[-0.767,1.416],[-0.616,1.544],[-0.631,1.151],[-0.757,1.324],[-5.008,10.858],[6.28,0.754],[0.78,-4.06],[0.405,-4.988],[8.39,-15.404],[1.187,-21.552]],\"v\":[[47,-109],[42.544,-108.226],[40,-107],[37.952,-106.544],[36,-106],[31.953,-102.099],[29,-98],[28.025,-97.494],[27,-97],[23.774,-90.499],[23,-84],[26.753,-78.951],[35,-77],[43.044,-83.9],[49,-88],[50.246,-84.288],[51,-78],[50.358,-72.392],[49,-67],[42.853,-52.155],[36,-41],[34.003,-36.492],[32,-32],[29,-29],[27,-24],[32,14],[44,6],[42,-6],[49,-23],[71,-75]],\"c\":true}],\"h\":1},{\"t\":35,\"s\":[{\"i\":[[8.043,-0.886],[1.416,-0.578],[2.278,-1.352],[1.236,-0.627],[0.831,-0.763],[0.311,-0.547],[0.347,-0.438],[0.627,-2.982],[-3.135,-2.767],[-3.115,2.252],[-2.844,2.136],[-1.146,-0.796],[-0.7,-3.858],[2.241,-5.103],[2.163,-3.381],[0.336,-0.745],[0.319,-0.495],[4.168,-7.075],[0.392,-6.585],[-1.84,-4.18],[-4.893,0],[-1.309,1.377],[-0.485,1.491],[0.519,2.006],[-0.269,2.498],[-1.51,2.797],[-1.745,2.835],[-3.084,4.604],[-2.081,10.716],[5.726,6.344]],\"o\":[[-3.056,0.337],[-1.416,0.578],[-1.225,0.727],[-1.236,0.627],[-0.408,0.374],[-0.311,0.547],[-2.626,3.307],[-0.627,2.981],[4.694,4.144],[3.115,-2.252],[2.716,-2.041],[1.146,0.796],[1.126,6.206],[-2.241,5.103],[-0.337,0.526],[-0.336,0.745],[-3.936,6.116],[-4.168,7.075],[-0.255,4.279],[1.841,4.18],[3.279,0],[1.309,-1.377],[0.806,-2.478],[-0.519,-2.006],[0.213,-1.978],[1.511,-2.797],[3.564,-5.791],[5.859,-8.748],[2.529,-13.025],[-5.149,-5.706]],\"v\":[[54,-109],[47.916,-107.762],[43,-105],[39.204,-103.026],[36,-101],[34.955,-99.548],[34,-98],[28.68,-88.595],[32,-80],[43.387,-78.79],[52,-87],[57.513,-88.924],[60,-82],[57.466,-64.882],[50,-52],[48.986,-49.976],[48,-48],[34.842,-27.852],[27,-7],[29.139,6.709],[39,14],[45.596,11.618],[48,7],[47.903,0.515],[47,-6],[49.851,-13.357],[55,-22],[67,-40],[80,-70],[72,-102]],\"c\":true}],\"h\":1},{\"t\":36,\"s\":[{\"i\":[[6.95,-0.74],[2.905,-1.098],[1.338,-1.282],[2.561,-3.354],[-1.377,-4.54],[-1.524,-1.099],[-2.451,-0.109],[-1.063,0.784],[-0.925,0.989],[-2.672,1.205],[-0.427,0.231],[-1.115,-0.377],[-0.221,-2.138],[2.655,-4.292],[0.852,-1.278],[0.353,-0.47],[0.554,-0.76],[0.376,-0.501],[0.552,-0.759],[1.638,-2.261],[2.119,-8.747],[-11.571,0.349],[0.102,-0.212],[-0.6,5.533],[-3.865,5.614],[-3.36,5.017],[-2.959,7.022],[-0.68,2.933],[8.858,3.993],[0.432,0.229]],\"o\":[[-3.041,0.324],[-2.905,1.098],[-2.132,2.042],[-2.561,3.354],[0.454,1.496],[1.524,1.099],[2.821,0.126],[1.064,-0.784],[2.484,-2.655],[0.357,-0.161],[1.608,-0.869],[1.776,0.601],[0.807,7.818],[-0.965,1.561],[-0.298,0.447],[-0.636,0.848],[-0.344,0.472],[-0.635,0.846],[-1.758,2.419],[-5.704,7.876],[-2.125,8.771],[5.64,-0.17],[2.416,-5.024],[0.401,-3.692],[4.493,-6.525],[4.477,-6.685],[1.175,-2.788],[3.523,-15.198],[-0.36,-0.162],[-3.543,-1.875]],\"v\":[[60,-109],[50.723,-106.719],[44,-103],[35.868,-94.873],[33,-83],[36.002,-78.96],[42,-77],[47.422,-78.164],[50,-81],[56,-86],[57,-88],[64,-90],[69,-80],[60,-57],[58,-52],[56,-51],[55,-48],[53,-47],[52,-44],[46,-37],[32,-11],[44,14],[53,9],[51,-6],[60,-21],[73,-39],[84,-59],[88,-68],[76,-105],[75,-107]],\"c\":true}],\"h\":1},{\"t\":37,\"s\":[{\"i\":[[6.233,-0.744],[3.673,-1.795],[2.536,-2.251],[1.463,-2.357],[-0.21,-2.367],[-1.895,-1.574],[-2.184,-0.066],[-3.996,3.091],[-3.467,-1.045],[-0.259,-4.087],[3.554,-5.557],[3.616,-4.531],[1.087,-12.139],[-9.903,1.954],[-0.682,3.684],[-0.71,4.488],[-0.964,1.734],[-0.939,1.446],[-0.345,0.461],[-0.554,0.76],[-0.376,0.501],[-0.596,0.775],[-1.455,2.193],[-0.623,0.94],[-0.623,0.94],[-0.506,0.737],[-0.962,1.445],[-0.981,1.538],[0.282,9.538],[7.628,4.07]],\"o\":[[-4.773,0.57],[-3.673,1.795],[-1.358,1.206],[-1.463,2.357],[0.234,2.637],[1.896,1.574],[6.258,0.189],[4.147,-3.208],[2.255,0.68],[0.397,6.272],[-3.899,6.097],[-9.421,11.805],[-1.145,12.792],[3.806,-0.751],[0.803,-4.341],[0.086,-0.546],[0.913,-1.643],[0.285,-0.438],[0.636,-0.848],[0.344,-0.472],[0.653,-0.87],[2.166,-2.819],[1.103,-1.662],[1.103,-1.662],[0.595,-0.897],[0.809,-1.177],[0.873,-1.312],[4.282,-6.717],[-0.374,-12.63],[-4.306,-2.297]],\"v\":[[65,-109],[52.323,-105.261],[43,-99],[38.324,-93.371],[36,-86],[39.537,-79.572],[46,-77],[57,-84],[70,-90],[76,-79],[68,-59],[56,-43],[35,-8],[50,14],[57,6],[55,-7],[58,-11],[60,-16],[62,-17],[63,-20],[65,-21],[66,-24],[74,-33],[77,-37],[80,-41],[82,-43],[84,-48],[87,-52],[96,-79],[81,-107]],\"c\":true}],\"h\":1},{\"t\":38,\"s\":[{\"i\":[[1.647,-0.211],[3.205,-1.278],[3.453,-2.54],[2.196,-2.853],[-1.45,-4.627],[-1.536,-1.121],[-2.416,-0.112],[-2.608,2.496],[-2.576,1.201],[-2.905,-1.487],[-0.312,-4.948],[1.855,-3.591],[1.77,-2.606],[2.135,-2.77],[2.131,-2.486],[4.115,-5.865],[0,-7.076],[-9.381,0.969],[0.945,4.244],[-0.331,2.895],[-2.557,3.213],[-1.725,2.071],[-2.393,3.051],[-0.908,1.137],[-1.532,2.219],[-0.672,1.075],[-1.198,3.937],[3.55,7.75],[6.098,3.388],[1.056,0.329]],\"o\":[[-2.727,0.349],[-3.205,1.278],[-2.665,1.96],[-2.196,2.853],[0.439,1.403],[1.536,1.121],[3.993,0.185],[2.608,-2.496],[4.069,-1.897],[2.905,1.487],[0.191,3.028],[-1.855,3.591],[-2.331,3.432],[-2.135,2.77],[-5.541,6.463],[-4.115,5.865],[0,10.245],[8.784,-0.907],[-0.459,-2.063],[0.63,-5.51],[1.77,-2.224],[3.326,-3.993],[0.909,-1.159],[1.718,-2.153],[0.596,-0.863],[2.908,-4.652],[3.053,-10.033],[-3.703,-8.083],[-0.968,-0.538],[-4.187,-1.303]],\"v\":[[69,-109],[60.044,-106.643],[50,-101],[41.914,-94.001],[40,-83],[43.017,-79.032],[49,-77],[58.562,-81.461],[66,-88],[76.817,-88.634],[82,-79],[78.971,-68.684],[73,-59],[66.35,-49.791],[60,-42],[44.844,-23.959],[38,-5],[52,14],[60,1],[58,-6],[68,-20],[73,-27],[82,-37],[84,-41],[90,-47],[92,-51],[100,-66],[98,-93],[86,-106],[82,-109]],\"c\":true}],\"h\":1},{\"t\":39,\"s\":[{\"i\":[[6.89,-0.815],[3.45,-1.257],[3.629,-2.669],[2.123,-2.867],[-1.331,-4.445],[-1.321,-1.204],[-2.45,-0.294],[-2.437,1.703],[-1.607,-13.824],[3.698,-5.29],[0.365,-0.487],[0.611,-0.78],[0.417,-0.448],[0.686,-0.803],[2.043,-2.273],[2.527,-10.638],[-10.055,1.039],[-0.351,0.734],[-0.86,5.47],[-1.778,2.471],[-3.623,4.033],[-1.799,1.999],[-1.727,2.054],[-0.83,1.103],[-0.403,0.433],[-0.679,0.936],[-0.711,1.062],[0.53,11.664],[4.486,4.612],[1.94,1.026]],\"o\":[[-2.573,0.304],[-3.45,1.257],[-2.84,2.088],[-2.123,2.867],[0.267,0.889],[1.321,1.204],[6.346,0.762],[8.352,-5.838],[0.651,5.598],[-0.322,0.461],[-0.659,0.878],[-0.361,0.46],[-0.697,0.749],[-2.476,2.9],[-10.39,11.564],[-3.517,14.807],[4.645,-0.48],[2.721,-5.69],[0.214,-1.358],[3.702,-5.144],[2.269,-2.525],[1.844,-2.05],[0.988,-1.175],[0.337,-0.448],[0.745,-0.8],[0.829,-1.142],[4.953,-7.395],[-0.454,-9.995],[-2.485,-2.555],[-5.035,-2.663]],\"v\":[[73,-109],[63.792,-106.773],[53,-101],[44.872,-93.767],[43,-83],[45.362,-79.554],[51,-77],[64,-84],[87,-80],[79,-60],[77,-59],[76,-56],[74,-55],[73,-52],[66,-45],[42,-12],[55,14],[63,9],[61,-7],[66,-14],[77,-27],[83,-34],[89,-40],[91,-44],[93,-45],[94,-48],[97,-51],[107,-80],[97,-101],[91,-107]],\"c\":true}],\"h\":1},{\"t\":40,\"s\":[{\"i\":[[4.56,-0.479],[2.024,-0.262],[1.95,-0.748],[0.607,-0.565],[0.799,-0.444],[1.906,-1.337],[1.585,-2.101],[0.779,-1.504],[-0.362,-2.331],[-4.248,-0.316],[-3.926,2.486],[-1.573,-12.837],[4.023,-5.229],[6.173,-6.924],[2.693,-4.652],[0.019,-5.058],[-8.53,1.272],[-0.963,3.04],[0.206,2.491],[-0.396,3.016],[-1.573,2.228],[-3.55,3.963],[-1.701,1.835],[-3.405,4.389],[-0.712,0.971],[-1.534,2.568],[-1.537,4.72],[-0.22,2.206],[6.333,4.201],[0.665,0.404]],\"o\":[[-1.954,0.206],[-2.024,0.262],[-0.772,0.296],[-0.607,0.565],[-2.79,1.549],[-1.906,1.337],[-1.247,1.653],[-0.779,1.504],[0.802,5.157],[6.652,0.495],[9.988,-6.325],[0.794,6.479],[-5.783,7.516],[-4.146,4.65],[-2.608,4.505],[-0.039,10.342],[5.007,-0.747],[0.615,-1.941],[-0.203,-2.447],[0.19,-1.452],[3.567,-5.052],[2.286,-2.552],[3.581,-3.864],[0.608,-0.783],[2.013,-2.745],[2.329,-3.899],[0.731,-2.245],[1.388,-13.905],[-0.823,-0.546],[-5.657,-3.436]],\"v\":[[77,-109],[70.996,-108.407],[65,-107],[63.02,-105.611],[61,-104],[54.097,-99.914],[49,-95],[45.793,-90.508],[45,-85],[54,-77],[67,-85],[91,-80],[82,-59],[60,-35],[48,-20],[43,-5],[58,14],[65,7],[66,2],[63,-6],[68,-13],[79,-26],[85,-33],[97,-45],[99,-49],[104,-56],[109,-67],[111,-74],[99,-104],[97,-106]],\"c\":true}],\"h\":1},{\"t\":41,\"s\":[{\"i\":[[5.891,-0.619],[7.054,-4.41],[-0.705,-7.194],[-5.031,-0.092],[-2.824,1.828],[-0.657,0.394],[-2.013,0.636],[-2.125,-0.552],[-0.418,-5.349],[2.56,-3.348],[0.67,-0.82],[2.544,-2.78],[1.253,-1.437],[1.207,-1.257],[0,-14.229],[-8.933,0.442],[-0.698,1.155],[0.579,3.606],[-0.339,2.536],[-0.446,0.479],[-0.686,0.877],[-0.919,0.987],[-0.682,0.766],[-5.785,6.618],[-0.808,0.985],[-0.432,0.464],[-0.687,0.872],[-1.654,2.521],[-1.364,6.519],[8.91,4.888]],\"o\":[[-6.357,0.668],[-5.62,3.514],[0.498,5.084],[5.931,0.109],[0.694,-0.449],[1.764,-1.058],[2.338,-0.739],[2.344,0.608],[0.506,6.467],[-0.612,0.801],[-2.183,2.67],[-1.231,1.345],[-1.246,1.429],[-12.272,12.774],[0,8.527],[2.795,-0.138],[0.685,-1.133],[-0.352,-2.197],[0.226,-1.696],[0.726,-0.779],[1.372,-1.753],[0.681,-0.732],[7.305,-8.207],[0.724,-0.829],[0.388,-0.473],[0.724,-0.777],[1.893,-2.404],[3.54,-5.395],[3.454,-16.502],[-6.286,-3.448]],\"v\":[[80,-109],[59,-102],[47,-86],[57,-77],[68,-84],[70,-85],[77,-89],[87,-90],[95,-79],[86,-61],[85,-58],[77,-51],[74,-46],[70,-42],[45,-5],[59,14],[67,10],[68,1],[65,-6],[69,-11],[70,-14],[75,-18],[76,-21],[96,-40],[98,-44],[100,-45],[101,-48],[107,-55],[114,-71],[99,-106]],\"c\":true}],\"h\":1},{\"t\":42,\"s\":[{\"i\":[[5.581,-0.632],[7.165,-4.053],[-1.142,-7.166],[-1.553,-1.381],[-2.421,-0.18],[-1.8,1.565],[-1.521,0.984],[-3.539,1.312],[-2.811,-0.562],[-1.738,-1.872],[-0.263,-3.155],[2.61,-4.713],[9.48,-10.878],[1.233,-1.504],[1.8,-3.22],[0.368,-5.272],[-1.843,-2.869],[-6.806,1.534],[-0.796,2.513],[0.246,2.439],[-0.489,3.636],[-0.368,0.49],[-0.662,0.792],[-3.438,3.791],[-5.613,8.462],[-1.561,2.724],[-1.17,6.131],[7.022,5.403],[0.481,0.217],[0.411,0.238]],\"o\":[[-5.48,0.621],[-7.166,4.053],[0.366,2.296],[1.553,1.381],[3.279,0.244],[1.8,-1.565],[2.031,-1.314],[3.54,-1.312],[1.785,0.357],[1.738,1.872],[0.371,4.452],[-8.341,15.06],[-1.324,1.52],[-2.438,2.974],[-1.597,2.857],[-0.327,4.68],[1.338,2.084],[3.671,-0.828],[0.639,-2.018],[-0.224,-2.216],[0.192,-1.43],[0.677,-0.903],[3.676,-4.395],[7.576,-8.354],[1.656,-2.497],[2.724,-4.753],[2.594,-13.588],[-0.41,-0.315],[-0.349,-0.157],[-5.679,-3.284]],\"v\":[[82,-109],[60.534,-101.908],[49,-85],[51.959,-79.413],[58,-77],[65.318,-79.579],[70,-84],[78.915,-88.407],[89,-90],[94.642,-86.599],[98,-79],[94,-68],[61,-32],[58,-27],[51,-19],[47,-7],[50,8],[63,14],[69,7],[70,2],[67,-6],[71,-11],[72,-14],[85,-27],[108,-52],[112,-59],[117,-72],[106,-104],[104,-104],[103,-106]],\"c\":true}],\"h\":1},{\"t\":43,\"s\":[{\"i\":[[4.927,-0.558],[3.542,-1.123],[3.644,-2.146],[2.59,-2.62],[-0.351,-3.759],[-1.728,-1.527],[-1.838,-0.203],[-1.693,1.27],[-1.334,1.242],[-1.264,0.661],[-0.923,0.407],[-1.021,0.334],[-1.016,0.265],[-2.77,-1.549],[-1.102,-4.061],[2.077,-3.671],[2.572,-3.066],[2.696,-2.589],[1.977,-1.977],[2.091,-10.911],[-10.779,1.307],[-1.057,1.787],[-1.051,5.198],[-2.459,2.941],[-0.93,1.141],[-3.209,2.975],[-2.158,2.158],[-2.093,2.308],[-2.789,7.123],[11.877,6.689]],\"o\":[[-3.189,0.361],[-3.542,1.123],[-2.991,1.761],[-2.59,2.62],[0.251,2.69],[1.728,1.527],[2.895,0.32],[1.693,-1.27],[1.02,-0.95],[1.264,-0.661],[0.9,-0.398],[1.021,-0.334],[4.816,-1.257],[2.77,1.549],[0.882,3.25],[-2.077,3.671],[-2.239,2.668],[-2.696,2.589],[-9.81,9.81],[-2.071,10.81],[1.08,-0.131],[3.745,-6.328],[0.488,-2.412],[0.944,-1.129],[2.981,-3.657],[2.545,-2.359],[2.037,-2.037],[5.832,-6.43],[6.585,-16.816],[-5.88,-3.312]],\"v\":[[84,-109],[73.841,-106.839],[63,-102],[53.993,-95.498],[50,-86],[53.309,-79.635],[59,-77],[65.671,-78.829],[70,-83],[73.573,-85.407],[77,-87],[79.913,-88.099],[83,-89],[94.285,-88.489],[100,-80],[97.591,-69.362],[90,-59],[82.303,-50.981],[75,-44],[49,-10],[63,14],[70,10],[69,-7],[75,-14],[77,-18],[87,-28],[95,-34],[101,-41],[118,-64],[105,-106]],\"c\":true}],\"h\":1},{\"t\":44,\"s\":[{\"i\":[[5.115,-0.566],[0.943,-0.154],[1.191,-0.267],[0.914,-0.327],[1.32,-0.463],[1.941,-0.604],[1.595,-1.01],[0.14,-0.424],[0.226,-0.169],[0.793,-0.671],[0.484,-0.704],[-9.232,-0.443],[-2.197,1.257],[-5.522,-1.014],[-0.547,-6.508],[2.724,-4.107],[3.049,-2.882],[3.51,-5.292],[1.328,-2.294],[0.363,-1.894],[-1.376,-2.868],[-5.947,0.179],[-0.63,1.987],[-1.123,6.386],[-1.55,1.785],[-9.427,10.506],[-1.063,1.345],[-1.763,2.75],[0.099,8.822],[7.131,4.016]],\"o\":[[-1.038,0.115],[-0.943,0.154],[-1.026,0.231],[-0.914,0.327],[-1.639,0.575],[-1.941,0.604],[-0.215,0.136],[-0.14,0.424],[-0.907,0.68],[-1.156,0.978],[-5.414,7.879],[4.964,0.238],[4.374,-2.503],[3.374,0.62],[0.679,8.082],[-3.097,4.669],[-5.604,5.298],[-2.105,3.173],[-1.447,2.499],[-1.606,8.373],[2.312,4.819],[6.404,-0.193],[2.077,-6.55],[0.135,-0.768],[10.333,-11.897],[0.89,-0.991],[2.309,-2.921],[3.774,-5.887],[-0.135,-11.968],[-6.041,-3.402]],\"v\":[[86,-109],[83.114,-108.614],[80,-108],[77.22,-107.174],[74,-106],[68.467,-104.326],[63,-102],[62.508,-101.025],[62,-100],[59,-99],[55,-95],[61,-77],[75,-85],[93,-90],[103,-79],[91,-58],[78,-44],[61,-28],[54,-19],[50,-11],[52,6],[63,14],[72,7],[70,-7],[75,-14],[107,-45],[109,-49],[115,-56],[123,-79],[107,-106]],\"c\":true}],\"h\":1},{\"t\":45,\"s\":[{\"i\":[[0.261,-0.024],[8.017,-4.31],[-2.441,-8.274],[-1.328,-1.18],[-2.397,-0.288],[-2.528,1.81],[-1.946,1.114],[-3.131,1.036],[-3.078,-0.516],[-1.994,-1.612],[-0.453,-3.289],[2.299,-3.506],[2.393,-2.681],[2.722,-2.635],[2.206,-2.206],[2.769,-2.663],[2.362,-2.786],[2.18,-3.757],[0.046,-4.341],[-2.666,-3.677],[-4.385,0.463],[1.056,5.63],[-0.76,4],[-2.669,2.987],[-6.78,6.435],[-3.89,11.92],[3.755,5.161],[2.881,1.299],[0.428,0.231],[7.572,0.345]],\"o\":[[-5.371,0.485],[-8.018,4.31],[0.293,0.992],[1.328,1.18],[2.942,0.353],[2.528,-1.81],[2.397,-1.371],[3.131,-1.036],[1.571,0.263],[1.994,1.612],[0.59,4.296],[-2.299,3.506],[-2.904,3.254],[-2.722,2.635],[-2.564,2.564],[-2.769,2.663],[-2.232,2.632],[-2.181,3.757],[-0.05,4.755],[2.666,3.677],[7.589,-0.801],[-0.441,-2.352],[0.115,-0.608],[6.683,-7.478],[10.619,-10.079],[3.595,-11.016],[-2.432,-3.343],[-0.358,-0.161],[-4.269,-2.305],[-0.985,-0.045]],\"v\":[[88,-109],[64.641,-101.842],[53,-83],[55.422,-79.472],[61,-77],[69.247,-79.9],[76,-85],[84.489,-88.916],[94,-90],[99.839,-87.269],[104,-80],[100.738,-68.289],[93,-59],[84.476,-50.214],[77,-43],[68.849,-35.167],[61,-27],[53.861,-17.282],[50,-5],[54.174,8.414],[65,14],[73,1],[71,-7],[77,-15],[99,-36],[123,-69],[118,-97],[110,-104],[109,-106],[90,-110]],\"c\":true}],\"h\":1},{\"t\":46,\"s\":[{\"i\":[[0.261,-0.024],[3.609,-1.108],[3.998,-2.245],[-0.756,-8.148],[-4.586,-0.172],[-3.519,2.276],[-3.334,0.953],[-4.742,-2.039],[-0.147,-6.015],[1.516,-2.355],[5.212,-4.928],[2.644,-2.52],[2.302,-2.807],[1.252,-1.683],[0.027,-7.326],[-9.201,1.116],[1.156,7.206],[-0.386,2.527],[-2.006,2.278],[-4.659,4.126],[-1.031,0.939],[-4.081,4.977],[-0.432,0.464],[-0.685,0.891],[-1.167,2.216],[0.124,6.681],[1.678,2.899],[2.982,2.713],[0.823,0.444],[6.03,0.275]],\"o\":[[-3.566,0.322],[-3.609,1.108],[-6.202,3.483],[0.531,5.729],[7.273,0.272],[2.833,-1.832],[3.97,-1.135],[1.682,0.723],[0.086,3.512],[-5.448,8.46],[-2.964,2.802],[-3.241,3.089],[-1.422,1.734],[-3.514,4.726],[-0.035,9.359],[6.251,-0.758],[-0.396,-2.471],[0.249,-1.627],[4.982,-5.658],[1.278,-1.132],[5.173,-4.711],[0.388,-0.473],[0.73,-0.785],[2.117,-2.754],[2.635,-5.004],[-0.117,-6.315],[-2.072,-3.579],[-2.029,-1.847],[-4.47,-2.413],[-0.985,-0.045]],\"v\":[[89,-109],[78.324,-106.942],[67,-102],[53,-86],[63,-77],[73,-83],[84,-88],[99,-89],[106,-78],[101,-67],[78,-42],[69,-35],[62,-26],[58,-22],[51,-5],[66,14],[74,1],[71,-6],[77,-13],[93,-29],[97,-32],[112,-49],[114,-50],[115,-53],[121,-60],[126,-79],[121,-94],[115,-101],[110,-106],[91,-110]],\"c\":true}],\"h\":1},{\"t\":47,\"s\":[{\"i\":[[0.256,-0.022],[2.254,-0.585],[3.206,-1.332],[1.791,-0.633],[1.243,-0.803],[0.459,-0.344],[0.796,-0.69],[0.578,-2.536],[-5.412,-0.37],[-5.389,2.961],[-5.612,-7.141],[4.373,-4.9],[5.997,-4.909],[3.55,-3.74],[1.212,-6.251],[-3.531,-3.523],[-4.555,1.079],[-0.436,0.887],[-0.723,4.885],[-1.207,1.819],[-1.929,1.28],[-0.83,0.751],[-7.632,9.27],[-2.211,5.316],[-0.595,2.568],[2.654,4.923],[0.466,0.486],[3.364,1.516],[0.428,0.231],[8.55,0.39]],\"o\":[[-3.78,0.33],[-2.254,0.585],[-1.595,0.663],[-1.791,0.633],[-0.437,0.283],[-0.915,0.687],[-2.477,2.147],[-1.81,7.935],[5.485,0.375],[9.229,-5.071],[7.221,9.187],[-7.227,8.097],[-4.679,3.831],[-5.316,5.6],[-1.874,9.669],[2.82,2.814],[1.501,-0.355],[3.521,-7.157],[0.248,-1.678],[1.85,-2.789],[1.033,-0.685],[9.235,-8.355],[4.474,-5.434],[0.69,-1.658],[2.05,-8.853],[-0.787,-1.461],[-2.878,-3.004],[-0.358,-0.161],[-4.426,-2.389],[-0.981,-0.045]],\"v\":[[90,-109],[81.569,-107.752],[74,-105],[68.736,-103.105],[64,-101],[63,-99],[60,-98],[54,-90],[63,-77],[78,-85],[104,-85],[96,-60],[77,-41],[65,-29],[52,-11],[57,10],[68,14],[74,9],[72,-6],[79,-15],[86,-22],[88,-24],[112,-48],[124,-64],[126,-71],[123,-92],[121,-96],[112,-104],[111,-106],[92,-110]],\"c\":true}],\"h\":1},{\"t\":48,\"s\":[{\"i\":[[5.707,-0.548],[3.607,-1.102],[4.005,-2.249],[2.819,-2.546],[-0.378,-4.074],[-1.662,-1.648],[-2.151,-0.238],[-1.707,1.271],[-1.321,1.23],[-4.305,1.431],[-2.995,-0.388],[-2.217,-1.571],[-0.439,-4.495],[2.592,-3.463],[2.102,-2.328],[3.975,-3.638],[4.112,-3.924],[2.697,-3.058],[1.5,-3.18],[0.57,-2.285],[-0.267,-2.771],[-10.363,1.257],[1.29,7.424],[-0.455,2.952],[-2.67,2.951],[-4.807,4.372],[-3.133,3.133],[-1.813,14.684],[3.516,5.047],[5.309,2.786]],\"o\":[[-3.566,0.342],[-3.607,1.102],[-3.101,1.742],[-2.819,2.546],[0.201,2.169],[1.662,1.648],[2.852,0.316],[1.707,-1.271],[1.786,-1.663],[4.305,-1.431],[1.695,0.22],[2.216,1.571],[0.471,4.82],[-2.592,3.463],[-4.989,5.524],[-3.975,3.638],[-2.714,2.59],[-2.697,3.058],[-0.985,2.088],[-0.571,2.285],[0.413,4.288],[6.161,-0.747],[-0.406,-2.339],[0.384,-2.489],[5.442,-6.015],[2.741,-2.493],[10.566,-10.566],[1.415,-11.462],[-3.734,-5.361],[-5.857,-3.073]],\"v\":[[90,-109],[79.329,-106.93],[68,-102],[58.391,-95.749],[54,-86],[57.038,-80.051],[63,-77],[69.648,-78.841],[74,-83],[84.094,-88.039],[96,-90],[102.442,-87.706],[107,-79],[102.93,-66.631],[95,-58],[81.841,-44.8],[70,-34],[61.59,-25.442],[55,-16],[52.561,-9.512],[52,-2],[67,14],[75,1],[72,-6],[79,-14],[94,-29],[102,-37],[127,-73],[121,-96],[111,-106]],\"c\":true}],\"h\":1},{\"t\":49,\"s\":[{\"i\":[[3.726,-0.269],[3.866,-1.088],[4.144,-2.403],[2.823,-2.612],[-0.276,-3.822],[-1.586,-1.63],[-2.499,-0.282],[-1.548,1.293],[-1.59,1.192],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.118,-3.707],[1.76,-2.908],[6.757,-6.211],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-7.457,6.16],[-3.098,4.195],[3.88,12.637],[0.571,0.761],[0.508,0.74],[2.927,1.694],[0.556,0.251]],\"o\":[[-3.391,0.245],[-3.866,1.088],[-2.984,1.73],[-2.823,2.612],[0.159,2.197],[1.586,1.63],[3.218,0.363],[1.548,-1.293],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[2.825,2.405],[0.108,3.373],[-5.399,8.92],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[8.916,-8.463],[4.571,-3.776],[5.652,-7.653],[-0.646,-2.104],[-0.616,-0.821],[-3.524,-5.128],[-0.784,-0.454],[-4.8,-2.162]],\"v\":[[91,-109],[80.065,-107.118],[68,-102],[58.555,-95.569],[54,-86],[56.744,-80.063],[63,-77],[69.721,-78.833],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[108,-78],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[118,-54],[127,-87],[123,-93],[122,-96],[112,-105],[110,-107]],\"c\":true}],\"h\":1},{\"t\":50,\"s\":[{\"i\":[[10.014,-0.756],[0.334,0.01],[0.332,-0.013],[7.34,-4.098],[-0.047,-5.375],[-1.561,-1.849],[-2.735,-0.328],[-1.553,1.304],[-1.559,1.17],[-0.564,0.12],[-0.354,0.225],[-4.846,0.868],[-3.148,-2.679],[-0.605,-0.782],[-0.453,-0.912],[2.39,-3.712],[5.022,-4.916],[1.428,-1.092],[0.716,-0.65],[4.753,-5.254],[0.052,-8.184],[-9.639,1.902],[0.967,6.027],[-0.365,2.287],[-0.108,0.144],[-0.519,0.745],[-3.935,3.656],[-4.823,4.433],[-3.771,12.303],[9.393,5.781]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.69,0.232],[-7.339,4.098],[0.02,2.276],[1.561,1.849],[3.227,0.388],[1.553,-1.304],[0.392,-0.294],[0.564,-0.12],[3.47,-2.209],[4.846,-0.868],[1.127,0.96],[0.605,0.782],[2.799,5.636],[-4.487,6.967],[-2.004,1.962],[-0.743,0.568],[-5.103,4.635],[-4.541,5.021],[-0.059,9.36],[6.182,-1.22],[-0.434,-2.705],[0.005,-0.029],[0.621,-0.828],[3.433,-4.925],[6.122,-5.688],[10.354,-9.517],[4.825,-15.744],[-6.583,-4.051]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[67.197,-101.857],[54,-87],[56.464,-80.539],[63,-77],[69.751,-78.832],[74,-83],[75.529,-83.551],[77,-84],[90.242,-89.166],[103,-87],[105.506,-84.464],[107,-82],[103,-67],[88,-50],[82,-44],[79,-43],[63,-27],[52,-5],[68,14],[75,1],[72,-6],[74,-7],[75,-10],[86,-21],[101,-35],[126,-68],[114,-104]],\"c\":true}],\"h\":1},{\"t\":51,\"s\":[{\"i\":[[10.014,-0.756],[0.334,0.01],[0.332,-0.013],[7.385,-4.171],[-0.434,-6.076],[-1.652,-1.692],[-2.25,-0.27],[-3.046,2.238],[-3.59,1.436],[-3.744,0.083],[-2.191,-1.865],[-0.881,-1.501],[-0.057,-1.79],[1.061,-1.93],[0.82,-1.273],[2.519,-2.714],[2.61,-2.555],[1.071,-1.118],[0.714,-0.546],[0.716,-0.65],[4.921,-5.441],[0,-8.425],[-9.948,1.963],[0.967,6.027],[-0.361,2.289],[-1.893,2.135],[-2.46,2.285],[-4.891,4.496],[-3.77,12.301],[9.393,5.781]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.894,0.24],[-7.385,4.171],[0.141,1.96],[1.652,1.692],[3.227,0.388],[3.046,-2.238],[3.834,-1.534],[3.744,-0.083],[1.418,1.208],[0.881,1.501],[0.064,2.005],[-1.061,1.93],[-2.312,3.591],[-2.519,2.714],[-1.002,0.981],[-1.071,1.118],[-0.743,0.568],[-5.167,4.692],[-4.504,4.98],[0,9.142],[6.182,-1.22],[-0.434,-2.706],[0.105,-0.668],[2.217,-2.501],[6.163,-5.726],[10.351,-9.515],[4.825,-15.744],[-6.583,-4.051]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[66.754,-101.877],[54,-86],[56.918,-80.233],[63,-77],[72.227,-80.632],[82,-87],[93.732,-89.55],[103,-87],[106.521,-82.937],[108,-78],[106.163,-71.951],[103,-67],[95.723,-57.723],[88,-50],[84.784,-46.674],[82,-44],[79,-43],[63,-27],[52,-5],[68,14],[75,1],[72,-6],[78,-14],[86,-21],[101,-35],[126,-68],[114,-104]],\"c\":true}],\"h\":1},{\"t\":52,\"s\":[{\"i\":[[12.38,-0.935],[0.334,0.01],[0.332,-0.013],[7.395,-4.231],[-0.401,-5.84],[-1.589,-1.631],[-2.495,-0.281],[-1.556,1.296],[-1.569,1.177],[-0.708,0.451],[-6.344,-5.401],[-0.118,-3.707],[1.553,-2.412],[2.109,-2.261],[3.479,-3.032],[1.015,-0.925],[4.884,-5.399],[0.457,-0.597],[0.675,-0.881],[0.965,-1.73],[-4.653,-6.739],[-6.717,1.325],[0.967,6.027],[-0.361,2.289],[-1.893,2.135],[-2.46,2.285],[-4.813,4.442],[-3.763,12.278],[3.547,5.784],[3.366,3.062]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.822,0.237],[-7.395,4.231],[0.151,2.196],[1.589,1.631],[3.209,0.362],[1.556,-1.296],[0.784,-0.588],[6.92,-4.404],[2.825,2.405],[0.127,3.97],[-1.794,2.785],[-3.802,4.076],[-1.461,1.273],[-5.345,4.868],[-0.563,0.622],[-0.636,0.832],[-1.303,1.701],[-4.265,7.65],[2.215,3.208],[6.182,-1.22],[-0.434,-2.706],[0.105,-0.668],[2.217,-2.501],[6.121,-5.687],[10.358,-9.559],[2.752,-8.978],[-2.654,-4.329],[-7.422,-6.753]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[66.832,-101.702],[54,-86],[56.742,-80.064],[63,-77],[69.73,-78.846],[74,-83],[77,-84],[103,-87],[108,-78],[103,-67],[96,-58],[84,-46],[79,-43],[63,-27],[61,-25],[60,-22],[56,-18],[56,8],[68,14],[75,1],[72,-6],[78,-14],[86,-21],[101,-35],[126,-68],[124,-93],[117,-101]],\"c\":true}],\"h\":1},{\"t\":53,\"s\":[{\"i\":[[12.304,-0.929],[0.334,0.01],[0.332,-0.013],[7.404,-4.223],[-0.424,-5.87],[-1.586,-1.63],[-2.499,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[1.066,-1.956],[0.799,-1.242],[3.42,-3.683],[2.926,-2.55],[1.015,-0.925],[4.644,-5.08],[-8.281,-11.995],[-6.717,1.325],[0.967,6.027],[-0.361,2.289],[-1.837,2.073],[-2.369,2.222],[-4.859,4.485],[13.718,22.37],[3.366,3.062]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.404,4.223],[0.159,2.197],[1.586,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.061,1.934],[-1.066,1.956],[-2.394,3.718],[-3.42,3.683],[-1.461,1.273],[-5.408,4.924],[-7.65,8.367],[2.215,3.208],[6.182,-1.22],[-0.434,-2.706],[0.097,-0.617],[2.18,-2.459],[6.391,-5.993],[13.758,-12.697],[-2.654,-4.329],[-7.482,-6.808]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[66.828,-101.724],[54,-86],[56.744,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.146,-71.981],[103,-67],[93.899,-55.624],[84,-46],[79,-43],[63,-27],[56,8],[68,14],[75,1],[72,-6],[78,-14],[86,-21],[101,-35],[124,-93],[117,-101]],\"c\":true}],\"h\":1},{\"t\":54,\"s\":[{\"i\":[[12.304,-0.929],[0.334,0.01],[0.332,-0.013],[7.404,-4.223],[-0.424,-5.87],[-1.586,-1.63],[-2.499,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[1.066,-1.956],[0.799,-1.242],[3.42,-3.683],[2.926,-2.55],[1.015,-0.925],[4.644,-5.08],[-8.281,-11.995],[-6.717,1.325],[0.967,6.027],[-0.361,2.289],[-1.837,2.073],[-2.369,2.222],[-4.843,4.499],[13.699,22.339],[3.366,3.062]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.404,4.223],[0.159,2.197],[1.586,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.061,1.934],[-1.066,1.956],[-2.394,3.718],[-3.42,3.683],[-1.461,1.273],[-5.408,4.924],[-7.65,8.367],[2.215,3.208],[6.182,-1.22],[-0.434,-2.706],[0.097,-0.617],[2.18,-2.459],[6.39,-5.993],[13.777,-12.8],[-2.654,-4.329],[-7.482,-6.808]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[66.828,-101.724],[54,-86],[56.744,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.146,-71.981],[103,-67],[93.899,-55.624],[84,-46],[79,-43],[63,-27],[56,8],[68,14],[75,1],[72,-6],[78,-14],[86,-21],[101,-35],[124,-93],[117,-101]],\"c\":true}],\"h\":1},{\"t\":55,\"s\":[{\"i\":[[12.3,-0.929],[0.334,0.01],[0.332,-0.013],[7.404,-4.223],[-0.424,-5.87],[-1.586,-1.63],[-2.499,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-6.344,-5.401],[-0.118,-3.707],[1.809,-2.989],[6.743,-6.198],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.831,2.095],[-2.428,2.305],[-3.88,3.668],[-1.006,0.754],[-0.796,0.79],[-2.045,2.458],[-2.43,5.656],[-0.59,2.441],[2.858,4.937],[2.61,2.375]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.404,4.223],[0.159,2.197],[1.586,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[6.92,-4.404],[2.825,2.405],[0.107,3.347],[-5.399,8.921],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.213,-1.349],[2.331,-2.667],[5.725,-5.434],[1.796,-1.698],[0.952,-0.714],[2.279,-2.261],[4.543,-5.459],[0.729,-1.698],[2.288,-9.461],[-1.972,-3.406],[-7.468,-6.795]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[66.828,-101.724],[54,-86],[56.744,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[103,-87],[108,-78],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[99,-34],[104,-39],[107,-40],[113,-48],[125,-64],[127,-71],[123,-94],[117,-101]],\"c\":true}],\"h\":1},{\"t\":56,\"s\":[{\"i\":[[12.291,-0.928],[0.334,0.01],[0.332,-0.013],[7.404,-4.223],[-0.424,-5.87],[-1.585,-1.63],[-2.5,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.118,-3.707],[1.809,-2.989],[6.743,-6.198],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-5.434,5.059],[-0.68,0.664],[-2.148,2.482],[-1.904,2.579],[0.202,10.903],[1.709,2.951],[2.701,2.457]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.404,4.223],[0.159,2.197],[1.585,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[2.825,2.405],[0.107,3.347],[-5.399,8.921],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[7.595,-7.21],[0.69,-0.642],[2.343,-2.289],[2.151,-2.486],[4.975,-6.737],[-0.118,-6.386],[-1.971,-3.404],[-7.448,-6.777]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[66.828,-101.724],[54,-86],[56.744,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[108,-78],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[103,-38],[106,-39],[112,-47],[118,-54],[128,-79],[123,-94],[117,-101]],\"c\":true}],\"h\":1},{\"t\":57,\"s\":[{\"i\":[[12.291,-0.928],[0.334,0.01],[0.332,-0.013],[7.406,-4.223],[-0.432,-5.871],[-1.584,-1.63],[-2.502,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[0.787,-1.708],[0.904,-1.495],[6.663,-6.125],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-7.457,6.16],[-0.666,0.747],[0.293,15.788],[1.709,2.951],[2.701,2.457]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.406,4.223],[0.162,2.198],[1.584,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.053,1.673],[-0.787,1.708],[-5.368,8.869],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[8.916,-8.463],[0.788,-0.651],[8.101,-9.083],[-0.118,-6.386],[-1.971,-3.404],[-7.448,-6.777]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[66.821,-101.725],[54,-86],[56.745,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.719,-72.866],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[109,-43],[128,-79],[123,-94],[117,-101]],\"c\":true}],\"h\":1},{\"t\":58,\"s\":[{\"i\":[[12.291,-0.928],[0.334,0.01],[0.332,-0.013],[7.406,-4.223],[-0.432,-5.871],[-1.584,-1.63],[-2.502,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[0.787,-1.708],[0.904,-1.495],[6.663,-6.125],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-7.457,6.16],[-0.666,0.747],[0.293,15.788],[1.709,2.951],[2.701,2.457]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.406,4.223],[0.162,2.198],[1.584,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.053,1.673],[-0.787,1.708],[-5.368,8.869],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[8.916,-8.463],[0.788,-0.651],[8.101,-9.083],[-0.118,-6.386],[-1.971,-3.404],[-7.448,-6.777]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[66.821,-101.725],[54,-86],[56.745,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.719,-72.866],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[109,-43],[128,-79],[123,-94],[117,-101]],\"c\":true}],\"h\":1},{\"t\":59,\"s\":[{\"i\":[[0.233,-0.018],[0.334,0.01],[0.332,-0.013],[7.34,-4.074],[-0.087,-5.471],[-1.568,-1.799],[-2.689,-0.303],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.118,-3.707],[1.665,-2.75],[6.591,-6.058],[3.695,-10.333],[-0.208,-3.713],[-1.136,-1.439],[2.07,12.899],[-0.361,2.289],[-2.204,2.522],[-2.383,2.262],[-7.197,5.946],[-3.098,4.195],[0.194,10.463],[2.607,2.721],[3.203,1.444],[0.428,0.231],[6.327,0.288]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.729,0.234],[-7.339,4.074],[0.04,2.501],[1.568,1.799],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[2.825,2.405],[0.102,3.205],[-5.468,9.033],[-11.226,10.319],[-2.003,5.601],[0.162,2.892],[6.756,8.557],[-0.434,-2.706],[0.214,-1.36],[2.256,-2.581],[8.329,-7.906],[4.571,-3.776],[4.943,-6.693],[-0.111,-5.958],[-2.958,-3.088],[-0.358,-0.161],[-4.365,-2.357],[-0.963,-0.044]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[67.138,-101.928],[54,-87],[56.513,-80.352],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[108,-78],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[118,-54],[128,-79],[122,-96],[113,-104],[112,-106],[93,-110]],\"c\":true}],\"h\":1},{\"t\":60,\"s\":[{\"i\":[[12.291,-0.928],[0.334,0.01],[0.332,-0.013],[7.34,-4.074],[-0.087,-5.471],[-1.568,-1.799],[-2.689,-0.303],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[0.805,-1.756],[0.833,-1.375],[6.591,-6.058],[3.695,-10.333],[-0.208,-3.713],[-1.136,-1.439],[2.07,12.899],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-7.457,6.16],[-0.666,0.747],[0.293,15.788],[1.709,2.951],[2.701,2.457]],\"o\":[[-0.331,0.025],[-0.334,-0.01],[-5.729,0.234],[-7.339,4.074],[0.04,2.501],[1.568,1.799],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.051,1.602],[-0.805,1.756],[-5.468,9.033],[-11.226,10.319],[-2.003,5.601],[0.162,2.892],[6.756,8.557],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[8.916,-8.463],[0.788,-0.651],[8.101,-9.083],[-0.118,-6.386],[-1.971,-3.404],[-7.448,-6.777]],\"v\":[[91,-109],[90.001,-108.991],[89,-109],[67.138,-101.928],[54,-87],[56.513,-80.352],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.663,-72.829],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[109,-43],[128,-79],[123,-94],[117,-101]],\"c\":true}],\"h\":1}],\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":0,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.525490196078,0.270588235294,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Rectangle 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":31,\"op\":300,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"Shape Layer 4\",\"parent\":15,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0.016,54.049,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":18,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]},{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":24,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"t\":50,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]}],\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.525490196078,0.270588235294,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":20,\"ix\":5},\"lc\":2,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[43.313,-47.836],\"ix\":2},\"a\":{\"a\":0,\"k\":[43.313,-47.836],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":24,\"op\":31,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":4,\"nm\":\"Shape Layer 1\",\"parent\":15,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0.016,54.049,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":18,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]},{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":24,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"t\":50,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]}],\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.525490196078,0.270588235294,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":20,\"ix\":5},\"lc\":2,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[43.313,-47.836],\"ix\":2},\"a\":{\"a\":0,\"k\":[43.313,-47.836],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":18,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":9,\"ty\":4,\"nm\":\"Shape Layer 5\",\"parent\":15,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0.016,54.049,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[-100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":18,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]},{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":24,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"t\":50,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]}],\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.525490196078,0.270588235294,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":20,\"ix\":5},\"lc\":2,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-78.173,-47.836],\"ix\":2},\"a\":{\"a\":0,\"k\":[-78.173,-47.836],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":18,\"op\":24,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":10,\"ty\":4,\"nm\":\"Cup 2\",\"parent\":15,\"td\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0,0,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],\"o\":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],\"v\":[[-49.55,-73.91],[49.55,-73.91],[70.876,-52.583],[62.346,32.723],[-62.346,32.723],[-70.876,-52.583]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.705882370472,0.247058823705,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Cup\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":11,\"ty\":4,\"nm\":\"Star 4 :M\",\"parent\":15,\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[-225,-6.953,0],\"to\":[75,0,0],\"ti\":[-75,0,0]},{\"t\":50,\"s\":[225,-6.953,0]}],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[24.984,188.998,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[5.278,-3.874],[6.547,-0.032],[5.316,3.822],[2.054,6.217],[-1.993,6.237],[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237]],\"o\":[[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237],[5.278,-3.874],[6.547,-0.033],[5.316,3.822],[2.054,6.217],[-1.993,6.237]],\"v\":[[19.304,28.834],[0.146,23.68],[-18.962,29.022],[-19.98,9.209],[-30.965,-7.313],[-12.436,-14.404],[-0.118,-29.957],[12.352,-14.526],[30.95,-7.617],[20.128,9.011]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-200.016,188.998],\"ix\":2},\"a\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star 4\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[5.278,-3.874],[6.547,-0.032],[5.316,3.822],[2.054,6.217],[-1.993,6.237],[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237]],\"o\":[[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237],[5.278,-3.874],[6.547,-0.033],[5.316,3.822],[2.054,6.217],[-1.993,6.237]],\"v\":[[19.304,28.834],[0.146,23.68],[-18.962,29.022],[-19.98,9.209],[-30.965,-7.313],[-12.436,-14.404],[-0.118,-29.957],[12.352,-14.526],[30.95,-7.617],[20.128,9.011]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-50.016,188.998],\"ix\":2},\"a\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star 3\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":2,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[5.278,-3.874],[6.547,-0.032],[5.316,3.822],[2.054,6.217],[-1.993,6.237],[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237]],\"o\":[[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237],[5.278,-3.874],[6.547,-0.033],[5.316,3.822],[2.054,6.217],[-1.993,6.237]],\"v\":[[19.304,28.834],[0.146,23.68],[-18.962,29.022],[-19.98,9.209],[-30.965,-7.313],[-12.436,-14.404],[-0.118,-29.957],[12.352,-14.526],[30.95,-7.617],[20.128,9.011]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[99.984,188.998],\"ix\":2},\"a\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star 2\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":3,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[5.278,-3.874],[6.547,-0.032],[5.316,3.822],[2.054,6.217],[-1.993,6.237],[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237]],\"o\":[[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237],[5.278,-3.874],[6.547,-0.033],[5.316,3.822],[2.054,6.217],[-1.993,6.237]],\"v\":[[19.304,28.834],[0.146,23.68],[-18.962,29.022],[-19.98,9.209],[-30.965,-7.313],[-12.436,-14.404],[-0.118,-29.957],[12.352,-14.526],[30.95,-7.617],[20.128,9.011]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":2},\"a\":{\"a\":0,\"k\":[249.984,188.998],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Star\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":4,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":12,\"ty\":4,\"nm\":\"Black Stand 2\",\"parent\":14,\"td\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0,0,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[-24.605,0],[0,0],[18.303,0]],\"o\":[[-18.303,0],[0,0],[24.605,0],[0,0]],\"v\":[[-42.653,-29.114],[-53.962,29.114],[53.962,29.114],[42.653,-29.114]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.349019616842,0.345098048449,0.43137255311,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Black Stand\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":13,\"ty\":4,\"nm\":\"White Stand 4 :M\",\"parent\":14,\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[-225,-1.544,0],\"to\":[75,0,0],\"ti\":[-75,0,0]},{\"t\":50,\"s\":[225,-1.544,0]}],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[24.984,347.302,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-4.323,0],[0,0],[-1.582,-4.024],[0,0],[4.323,0],[0,0],[-1.582,4.024],[0,0]],\"o\":[[0,0],[4.323,0],[0,0],[1.582,4.024],[0,0],[-4.323,0],[0,0],[1.582,-4.024]],\"v\":[[-25.949,-12.268],[25.998,-12.268],[33.803,-4.464],[37.313,4.464],[31.758,12.268],[-32.174,12.268],[-37.263,4.464],[-33.753,-4.464]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-200.016,347.302],\"ix\":2},\"a\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand 4\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-4.323,0],[0,0],[-1.582,-4.024],[0,0],[4.323,0],[0,0],[-1.582,4.024],[0,0]],\"o\":[[0,0],[4.323,0],[0,0],[1.582,4.024],[0,0],[-4.323,0],[0,0],[1.582,-4.024]],\"v\":[[-25.949,-12.268],[25.998,-12.268],[33.803,-4.464],[37.313,4.464],[31.758,12.268],[-32.174,12.268],[-37.263,4.464],[-33.753,-4.464]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-50.016,347.302],\"ix\":2},\"a\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand 3\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":2,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-4.323,0],[0,0],[-1.582,-4.024],[0,0],[4.323,0],[0,0],[-1.582,4.024],[0,0]],\"o\":[[0,0],[4.323,0],[0,0],[1.582,4.024],[0,0],[-4.323,0],[0,0],[1.582,-4.024]],\"v\":[[-25.949,-12.268],[25.998,-12.268],[33.803,-4.464],[37.313,4.464],[31.758,12.268],[-32.174,12.268],[-37.263,4.464],[-33.753,-4.464]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[99.984,347.302],\"ix\":2},\"a\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand 2\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":3,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-4.323,0],[0,0],[-1.582,-4.024],[0,0],[4.323,0],[0,0],[-1.582,4.024],[0,0]],\"o\":[[0,0],[4.323,0],[0,0],[1.582,4.024],[0,0],[-4.323,0],[0,0],[1.582,-4.024]],\"v\":[[-25.949,-12.268],[25.998,-12.268],[33.803,-4.464],[37.313,4.464],[31.758,12.268],[-32.174,12.268],[-37.263,4.464],[-33.753,-4.464]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":2},\"a\":{\"a\":0,\"k\":[249.984,347.302],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"White Stand\",\"np\":1,\"cix\":2,\"bm\":0,\"ix\":4,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":14,\"ty\":4,\"nm\":\"Black Stand\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"k\":[{\"s\":[90],\"t\":2,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[88.052],\"t\":3,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[83.09],\"t\":4,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[75.985],\"t\":5,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[67.277],\"t\":6,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[57.336],\"t\":7,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[46.447],\"t\":8,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[34.86],\"t\":9,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[10.836],\"t\":11,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0],\"t\":12,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-6.514],\"t\":13,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-10.253],\"t\":14,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-11.772],\"t\":15,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-11.657],\"t\":16,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-10.457],\"t\":17,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-8.646],\"t\":18,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-6.599],\"t\":19,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-4.592],\"t\":20,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-2.804],\"t\":21,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-1.336],\"t\":22,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.223],\"t\":23,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.544],\"t\":24,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[1.006],\"t\":25,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[1.219],\"t\":26,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[1.245],\"t\":27,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[1.142],\"t\":28,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.963],\"t\":29,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.75],\"t\":30,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.535],\"t\":31,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.34],\"t\":32,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.176],\"t\":33,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.049],\"t\":34,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.04],\"t\":35,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.097],\"t\":36,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.125],\"t\":37,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.132],\"t\":38,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.124],\"t\":39,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.107],\"t\":40,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.085],\"t\":41,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.062],\"t\":42,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.041],\"t\":43,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.023],\"t\":44,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.008],\"t\":45,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.002],\"t\":46,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.009],\"t\":47,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.013],\"t\":48,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.014],\"t\":49,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.013],\"t\":50,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.012],\"t\":51,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.01],\"t\":52,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.007],\"t\":53,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.005],\"t\":54,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.003],\"t\":55,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0.001],\"t\":56,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0],\"t\":57,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.001],\"t\":58,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.001],\"t\":59,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.001],\"t\":60,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.001],\"t\":61,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.001],\"t\":62,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.001],\"t\":63,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[-0.001],\"t\":65,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0],\"t\":66,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0],\"t\":67,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0],\"t\":68,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}},{\"s\":[0],\"t\":69,\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0],\"y\":[0]}}]},\"p\":{\"k\":[{\"s\":[138.235,254.547,0],\"t\":0,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[143.584,250.368,0],\"t\":1,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[157.812,240.556,0],\"t\":2,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[179.791,229.215,0],\"t\":3,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[209.087,221.759,0],\"t\":4,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[243.189,225.873,0],\"t\":5,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[274.404,246.799,0],\"t\":6,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[294.84,281.274,0],\"t\":7,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[299.502,322.507,0],\"t\":8,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[282.589,360.014,0],\"t\":9,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[249.984,377.959,0],\"t\":10,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[228.111,384.013,0],\"t\":11,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[215.555,387.488,0],\"t\":12,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[210.454,388.9,0],\"t\":13,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[210.841,388.792,0],\"t\":14,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[214.869,387.678,0],\"t\":15,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[220.951,385.994,0],\"t\":16,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[227.823,384.092,0],\"t\":17,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[234.564,382.227,0],\"t\":18,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[240.567,380.565,0],\"t\":19,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[245.498,379.201,0],\"t\":20,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[249.235,378.166,0],\"t\":21,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[251.813,377.453,0],\"t\":22,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[253.364,377.023,0],\"t\":23,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[254.079,376.826,0],\"t\":24,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[254.164,376.802,0],\"t\":25,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[253.818,376.898,0],\"t\":26,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[253.217,377.064,0],\"t\":27,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[252.503,377.262,0],\"t\":28,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[251.782,377.461,0],\"t\":29,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[251.126,377.643,0],\"t\":30,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[250.576,377.795,0],\"t\":31,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[250.15,377.913,0],\"t\":32,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[249.849,377.996,0],\"t\":33,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[249.66,378.049,0],\"t\":34,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}},{\"s\":[249.909,377.98,0],\"t\":42,\"i\":{\"x\":1,\"y\":1},\"o\":{\"x\":0,\"y\":0}}],\"l\":2},\"a\":{\"a\":0,\"k\":[0,29.114,0],\"ix\":1,\"l\":2},\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,1]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0]},\"t\":0,\"s\":[0,0,100]},{\"t\":10,\"s\":[100,100,100]}],\"ix\":6,\"l\":2}},\"ao\":0,\"ef\":[{\"ty\":5,\"nm\":\"Elastic Controller\",\"np\":5,\"mn\":\"Pseudo/MDS Elastic Controller\",\"ix\":1,\"en\":1,\"ef\":[{\"ty\":0,\"nm\":\"Amplitude\",\"mn\":\"Pseudo/MDS Elastic Controller-0001\",\"ix\":1,\"v\":{\"a\":0,\"k\":20,\"ix\":1}},{\"ty\":0,\"nm\":\"Frequency\",\"mn\":\"Pseudo/MDS Elastic Controller-0002\",\"ix\":2,\"v\":{\"a\":0,\"k\":40,\"ix\":2}},{\"ty\":0,\"nm\":\"Decay\",\"mn\":\"Pseudo/MDS Elastic Controller-0003\",\"ix\":3,\"v\":{\"a\":0,\"k\":60,\"ix\":3}}]},{\"ty\":5,\"nm\":\"Elastic Controller 2\",\"np\":5,\"mn\":\"Pseudo/MDS Elastic Controller\",\"ix\":2,\"en\":1,\"ef\":[{\"ty\":0,\"nm\":\"Amplitude\",\"mn\":\"Pseudo/MDS Elastic Controller-0001\",\"ix\":1,\"v\":{\"a\":0,\"k\":20,\"ix\":1}},{\"ty\":0,\"nm\":\"Frequency\",\"mn\":\"Pseudo/MDS Elastic Controller-0002\",\"ix\":2,\"v\":{\"a\":0,\"k\":40,\"ix\":2}},{\"ty\":0,\"nm\":\"Decay\",\"mn\":\"Pseudo/MDS Elastic Controller-0003\",\"ix\":3,\"v\":{\"a\":0,\"k\":60,\"ix\":3}}]}],\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[-24.605,0],[0,0],[18.303,0]],\"o\":[[-18.303,0],[0,0],[24.605,0],[0,0]],\"v\":[[-42.653,-29.114],[-53.962,29.114],[53.962,29.114],[42.653,-29.114]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.349019616842,0.345098048449,0.43137255311,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Black Stand\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":15,\"ty\":4,\"nm\":\"Cup\",\"parent\":14,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0,-152.895,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],\"o\":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],\"v\":[[-49.55,-73.91],[49.55,-73.91],[70.876,-52.583],[62.346,32.723],[-62.346,32.723],[-70.876,-52.583]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.705882370472,0.247058823705,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Cup\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":16,\"ty\":4,\"nm\":\"Stand\",\"parent\":14,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0,-56.636,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[19.235,36.65],[0,0],[-15.853,-38.082],[0,0]],\"o\":[[0,0],[-20.405,35.342],[0,0],[17.561,-38.659]],\"v\":[[-33.841,-56.55],[33.841,-56.55],[25.31,56.55],[-25.31,56.55]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.525490224361,0.270588248968,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Stand\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":17,\"ty\":4,\"nm\":\"Shape Layer 3\",\"parent\":15,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0.016,54.049,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":18,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]},{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":24,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"t\":50,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]}],\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.525490196078,0.270588235294,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":20,\"ix\":5},\"lc\":2,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[43.313,-47.836],\"ix\":2},\"a\":{\"a\":0,\"k\":[43.313,-47.836],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":18,\"op\":24,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":18,\"ty\":4,\"nm\":\"Shape Layer 6\",\"parent\":15,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0.016,54.049,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[-100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":18,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]},{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":24,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"t\":50,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]}],\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.525490196078,0.270588235294,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":20,\"ix\":5},\"lc\":2,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-78.173,-47.836],\"ix\":2},\"a\":{\"a\":0,\"k\":[-78.173,-47.836],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":24,\"op\":310,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":19,\"ty\":4,\"nm\":\"Shape Layer 2\",\"parent\":15,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[0.016,54.049,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[-100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":10,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":18,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]},{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.333,\"y\":0},\"t\":24,\"s\":[{\"i\":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],\"o\":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],\"v\":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],\"c\":false}]},{\"t\":50,\"s\":[{\"i\":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],\"o\":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],\"v\":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],\"c\":false}]}],\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[1,0.525490196078,0.270588235294,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":20,\"ix\":5},\"lc\":2,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-78.173,-47.836],\"ix\":2},\"a\":{\"a\":0,\"k\":[-78.173,-47.836],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":18,\"st\":10,\"bm\":0},{\"ddd\":0,\"ind\":21,\"ty\":0,\"nm\":\"Pre-comp 1\",\"refId\":\"comp_2\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":60,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[250,250,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":500,\"h\":500,\"ip\":16,\"op\":316,\"st\":16,\"bm\":0},{\"ddd\":0,\"ind\":22,\"ty\":0,\"nm\":\"Pre-comp 1\",\"refId\":\"comp_2\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":45,\"ix\":10},\"p\":{\"a\":0,\"k\":[250,250,0],\"ix\":2,\"l\":2},\"a\":{\"a\":0,\"k\":[250,250,0],\"ix\":1,\"l\":2},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6,\"l\":2}},\"ao\":0,\"w\":500,\"h\":500,\"ip\":11,\"op\":311,\"st\":11,\"bm\":0}],\"markers\":[]}"
  },
  {
    "path": "core/src/main/res/raw/trophy_winner.json",
    "content": "{\"v\":\"5.5.7\",\"meta\":{\"g\":\"LottieFiles AE 0.1.20\",\"a\":\"Ronit Paul\",\"k\":\"Winner Trophy Confetti Stars\",\"d\":\"Winner Trophy\",\"tc\":\"\"},\"fr\":60,\"ip\":0,\"op\":150,\"w\":720,\"h\":600,\"nm\":\"Final\",\"ddd\":0,\"assets\":[{\"id\":\"comp_0\",\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":0,\"nm\":\"right c\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[318,358,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[256,256,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[-176.563,176.563,100],\"ix\":6}},\"ao\":0,\"w\":512,\"h\":512,\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":0,\"nm\":\"left c\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[402,358,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[256,256,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[176.563,176.563,100],\"ix\":6}},\"ao\":0,\"w\":512,\"h\":512,\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":0,\"nm\":\"left c\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[302,254,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[256,256,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[117.188,116.406,100],\"ix\":6}},\"ao\":0,\"w\":512,\"h\":512,\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":0,\"nm\":\"right c\",\"refId\":\"comp_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[462,300,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[256,256,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[-100,100,100],\"ix\":6}},\"ao\":0,\"w\":512,\"h\":512,\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":0,\"nm\":\"trophy\",\"refId\":\"comp_2\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[360,394,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[80,80,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[280,280,100],\"ix\":6}},\"ao\":0,\"w\":160,\"h\":160,\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":0,\"nm\":\"star\",\"refId\":\"comp_3\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[0]},{\"t\":1,\"s\":[100]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[360,300,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[400,400,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[89.5,89.5,100],\"ix\":6}},\"ao\":0,\"w\":800,\"h\":800,\"ip\":0,\"op\":720.720720720721,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":4,\"nm\":\"Layer 20 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[360,380,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[76.814,74.241,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[298.779,298.779,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[42.285,0],[0,-42.285],[-28.625,-11.086],[0,0],[0,32.54]],\"o\":[[-42.285,0],[0,32.54],[0,0],[28.626,-11.086],[0,-42.284]],\"v\":[[-0.001,-73.991],[-76.564,2.572],[-27.637,73.991],[27.635,73.991],[76.564,2.572]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.910000011968,0.952999997606,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[76.813,74.241],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0}]},{\"id\":\"comp_1\",\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"hong 2\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":200,\"s\":[100]},{\"t\":210,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.982},\"o\":{\"x\":0.333,\"y\":0},\"t\":0,\"s\":[4.25,506.406,0],\"to\":[0,0,0],\"ti\":[-90.5,-10.844,0]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.167,\"y\":0.472},\"t\":17,\"s\":[245.75,87.656,0],\"to\":[87.25,0.594,0],\"ti\":[-2.5,-73.781,0]},{\"t\":240,\"s\":[368.75,270.281,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[154.25,-31.594,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[150.969,-34.875],[150.375,-29.531],[157.906,-28.313],[158.125,-33.594]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.980392156863,0.462745098039,0.592156862745,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"vang cam 4\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":183,\"s\":[100]},{\"t\":193,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.982},\"o\":{\"x\":0.333,\"y\":0},\"t\":3,\"s\":[-11.016,484.75,0],\"to\":[0,0,0],\"ti\":[-100.391,1.125,0]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.167,\"y\":0.471},\"t\":20,\"s\":[191.234,115.625,0],\"to\":[119.353,-2.259,0],\"ti\":[0,0,0]},{\"t\":243,\"s\":[339.984,243.875,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[124.234,13.875,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[123.375,6.5],[116.469,17.5],[125.25,21.25],[132,10.375]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.737254901961,0.121568627451,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":3,\"op\":243,\"st\":3,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"xanh min 4\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":205,\"s\":[100]},{\"t\":215,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.983},\"o\":{\"x\":0.333,\"y\":0},\"t\":0,\"s\":[-0.078,512.859,0],\"to\":[-6.672,-133.859,0],\"ti\":[-111.453,-1.391,0]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.167,\"y\":0.536},\"t\":17,\"s\":[239.297,142.859,0],\"to\":[98.953,-3.359,0],\"ti\":[0,0,0]},{\"t\":240,\"s\":[364.422,266.484,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[75.922,18.484,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0.781,3.063],[1.313,-1.25],[-2.563,-0.406],[-1.156,1.094]],\"o\":[[-1.313,1.344],[0.469,2.656],[1.469,-1.188],[-2.844,-0.875]],\"v\":[[75.156,13.406],[70.594,18],[76.719,23.563],[81.25,19.281]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.188235294118,0.839215686275,0.686274509804,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"xanh duong 2\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":183,\"s\":[100]},{\"t\":193,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.982},\"o\":{\"x\":0.333,\"y\":0},\"t\":0,\"s\":[-2.234,516.563,0],\"to\":[12.792,-17.146,0],\"ti\":[-104.484,5.188,0]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.167,\"y\":0.452},\"t\":17,\"s\":[164.016,151.688,0],\"to\":[100.967,-16.137,0],\"ti\":[-1.833,-65.917,0]},{\"t\":240,\"s\":[319.766,258.938,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[20.266,28.938,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[21.969,23.219],[14.594,29.781],[18.563,34.656],[25.938,28.25]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.247058823529,0.403921568627,0.949019607843,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":4,\"nm\":\"do cam 2\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":175,\"s\":[100]},{\"t\":185,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.984},\"o\":{\"x\":0.333,\"y\":0},\"t\":0,\"s\":[0.422,511.531,0],\"to\":[0,0,0],\"ti\":[-87.328,-22.969,0]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.167,\"y\":0.617},\"t\":17,\"s\":[241.172,157.031,0],\"to\":[111.328,1.719,0],\"ti\":[0,0,0]},{\"t\":240,\"s\":[326.672,247.906,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[-167.828,29.156,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,3.344],[1.875,-1.844],[-3.781,-0.125],[-1.875,2]],\"o\":[[-1.625,1.625],[0.094,2.531],[1.688,-1.656],[-3.906,0.844]],\"v\":[[-167.875,23.719],[-173.906,29.563],[-167.875,34.594],[-161.75,28.25]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.505882352941,0.392156862745,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"vang cam 3\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":185,\"s\":[100]},{\"t\":195,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.982},\"o\":{\"x\":0.333,\"y\":0},\"t\":0,\"s\":[-1.891,515.426,0],\"to\":[0,0,0],\"ti\":[-130.391,-1.199,0]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.167,\"y\":0.474},\"t\":17,\"s\":[162.359,137.301,0],\"to\":[158.641,0.949,0],\"ti\":[0,0,0]},{\"t\":240,\"s\":[285.859,265.676,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[-120.641,-26.949,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[2.063,1.5],[0.688,-1.344],[-2.688,0.719],[-0.031,1.688]],\"o\":[[-0.719,1.406],[2,2.219],[0.031,-1.5],[-2.375,0.313]],\"v\":[[-123.156,-30.281],[-125.344,-26.156],[-116.063,-23.875],[-115.938,-28.563]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.737254901961,0.121568627451,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":4,\"nm\":\"xanh min 3\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":65,\"s\":[100]},{\"t\":75,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.983},\"o\":{\"x\":0.333,\"y\":0},\"t\":0,\"s\":[3.703,511.641,0],\"to\":[0,0,0],\"ti\":[-290.672,-0.234,0]},{\"i\":{\"x\":0.667,\"y\":1},\"o\":{\"x\":0.167,\"y\":0.537},\"t\":17,\"s\":[275.203,139.141,0],\"to\":[98.672,-0.766,0],\"ti\":[0,0,0]},{\"t\":240,\"s\":[423.703,272.141,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[-179.781,-30.328,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[-180.016,-34.984],[-183.828,-31.438],[-179.547,-25.672],[-175.734,-29.141]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.1882353127,0.839215755463,0.686274528503,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0}]},{\"id\":\"comp_2\",\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"Layer 3 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[131.272,32.19,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[15.871,11.584,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,-2.258],[-0.19,-0.484],[0.543,0.015],[0.361,-2.112],[0,0],[6.917,0.199],[0,0],[-0.053,1.891],[-1.885,-0.045],[0,0],[-0.613,3.46],[0,0],[-5.527,-0.149],[-1.478,-0.8]],\"o\":[[0,0.55],[-0.466,-0.19],[-2.142,-0.05],[0,0],[-1.201,6.778],[0,0],[-1.892,-0.052],[0.051,-1.891],[0,0],[3.557,0.1],[0,0],[0.931,-5.451],[1.817,0.05],[-2.213,0.174]],\"v\":[[11.667,-5.577],[11.964,-4.018],[10.447,-4.337],[6.072,-0.738],[6.018,-0.417],[-8.229,11.135],[-12.24,11.025],[-15.568,7.505],[-12.05,4.176],[-8.039,4.288],[-0.73,-1.609],[-0.68,-1.893],[10.637,-11.186],[15.621,-9.855]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.702000038297,0.195999998205,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[15.872,11.584],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"Layer 4 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[123.409,66.361,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[30.677,45.756,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-1.595,1.016],[1.015,1.595],[-4.112,0],[-0.051,-0.001],[0,0],[-2.334,2.307],[-2.214,11.779],[6.885,0.192],[0.932,-5.451],[0,0],[3.557,0.1],[0,0],[0.051,-1.891],[-1.892,-0.052],[0,0],[-1.201,6.779],[0,0],[-2.142,-0.05],[0.484,-2.575],[24.219,-23.939],[1.399,0.042],[0,0],[-4.98,-7.816]],\"o\":[[1.596,-1.017],[-2.07,-3.25],[0.051,0],[0,0],[3.266,0.092],[25.543,-25.248],[1.267,-6.749],[-5.528,-0.148],[0,0],[-0.613,3.461],[0,0],[-1.885,-0.046],[-0.053,1.891],[0,0],[6.918,0.198],[0,0],[0.361,-2.113],[2.714,0.076],[-2.067,10.993],[-1.01,1],[0,0],[-9.81,-0.271],[1.017,1.598]],\"v\":[[-20.719,44.49],[-19.67,39.76],[-15.178,31.932],[-15.025,31.934],[-14.939,31.937],[-6.125,28.451],[29.161,-32.107],[18.501,-45.358],[7.182,-36.065],[7.133,-35.782],[-0.176,-29.884],[-4.188,-29.995],[-7.706,-26.666],[-4.377,-23.147],[-0.367,-23.036],[13.881,-34.589],[13.936,-34.909],[18.31,-38.509],[22.428,-33.372],[-10.941,23.577],[-14.749,25.089],[-14.835,25.087],[-25.448,43.441]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.866999966491,0.250999989229,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[30.677,45.756],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"Layer 5 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[28.727,32.19,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[15.872,11.584,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,-2.258],[0.189,-0.484],[-0.543,0.015],[-0.361,-2.112],[0,0],[-6.917,0.199],[0,0],[0.052,1.891],[1.885,-0.045],[0,0],[0.613,3.46],[0,0],[5.527,-0.148],[1.48,-0.799]],\"o\":[[0,0.55],[0.464,-0.19],[2.144,-0.05],[0,0],[1.199,6.778],[0,0],[1.891,-0.052],[-0.052,-1.891],[0,0],[-3.557,0.1],[0,0],[-0.93,-5.451],[-1.818,0.051],[2.214,0.175]],\"v\":[[-11.666,-5.577],[-11.962,-4.018],[-10.447,-4.337],[-6.072,-0.738],[-6.016,-0.417],[8.23,11.135],[12.241,11.025],[15.57,7.505],[12.051,4.176],[8.04,4.288],[0.73,-1.609],[0.681,-1.893],[-10.637,-11.186],[-15.622,-9.857]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.702000038297,0.195999998205,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[15.872,11.584],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"Layer 6 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[97.823,51.806,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[6.364,6.364,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-3.377,0],[0,3.377],[3.376,0],[0,-3.376]],\"o\":[[3.376,0],[0,-3.376],[-3.377,0],[0,3.377]],\"v\":[[0.001,6.114],[6.114,0],[0.001,-6.114],[-6.114,0]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.913999968884,0.552999997606,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[6.363,6.364],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":4,\"nm\":\"Layer 7 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[86.422,58.994,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[3.661,3.662,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-1.885,0],[0,1.885],[1.884,0],[0,-1.883]],\"o\":[[1.884,0],[0,-1.883],[-1.885,0],[0,1.885]],\"v\":[[0.001,3.411],[3.411,-0.001],[0.001,-3.411],[-3.411,-0.001]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.913999968884,0.552999997606,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[3.661,3.662],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"Layer 8 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[82.114,113.775,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[3.661,3.66,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-1.884,0],[0,1.884],[1.884,0],[0,-1.885]],\"o\":[[1.884,0],[0,-1.885],[-1.884,0],[0,1.884]],\"v\":[[0.001,3.411],[3.411,0.001],[0.001,-3.411],[-3.411,0.001]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.913999968884,0.552999997606,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[3.661,3.66],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":4,\"nm\":\"Layer 9 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[36.59,66.361,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[30.678,45.756,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[1.594,1.016],[-1.016,1.595],[4.113,0],[0.051,-0.001],[0,0],[2.334,2.307],[2.213,11.779],[-6.886,0.192],[-0.931,-5.451],[0,0],[-3.557,0.1],[0,0],[-0.052,-1.891],[1.891,-0.052],[0,0],[1.2,6.779],[0,0],[2.143,-0.05],[-0.484,-2.575],[-24.22,-23.939],[-1.4,0.042],[0,0],[4.979,-7.816]],\"o\":[[-1.596,-1.017],[2.07,-3.25],[-0.051,0],[0,0],[-3.265,0.092],[-25.544,-25.248],[-1.269,-6.749],[5.527,-0.148],[0,0],[0.612,3.461],[0,0],[1.886,-0.046],[0.053,1.891],[0,0],[-6.918,0.198],[0,0],[-0.36,-2.113],[-2.714,0.076],[2.066,10.993],[1.011,1],[0,0],[9.809,-0.271],[-1.017,1.598]],\"v\":[[20.72,44.49],[19.671,39.76],[15.178,31.932],[15.025,31.934],[14.939,31.937],[6.126,28.451],[-29.159,-32.107],[-18.499,-45.358],[-7.181,-36.065],[-7.132,-35.782],[0.178,-29.884],[4.188,-29.995],[7.707,-26.666],[4.378,-23.147],[0.368,-23.036],[-13.879,-34.589],[-13.935,-34.909],[-18.309,-38.509],[-22.426,-33.372],[10.942,23.577],[14.75,25.089],[14.836,25.087],[25.449,43.441]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.866999966491,0.250999989229,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[30.678,45.756],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":8,\"ty\":4,\"nm\":\"Layer 10 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[52.663,28.588,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[16.074,4.875,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,2.554],[-2.555,0],[0,0],[0,-2.554],[-2.554,0],[0,0]],\"o\":[[0,-2.554],[0,0],[-2.554,0],[0,2.554],[0,0],[-2.555,0]],\"v\":[[11.199,-0.001],[15.824,-4.624],[-11.2,-4.624],[-15.824,-0.001],[-11.2,4.624],[15.824,4.624]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.866999966491,0.250999989229,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[16.074,4.875],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":9,\"ty\":4,\"nm\":\"Layer 11 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[80,28.588,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[43.411,4.875,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[2.554,0],[0,0],[0,2.555],[-2.554,0],[0,0],[0,-2.554]],\"o\":[[0,0],[-2.554,0],[0,-2.554],[0,0],[2.554,0],[0,2.554]],\"v\":[[38.537,4.624],[-38.537,4.624],[-43.161,-0.001],[-38.537,-4.624],[38.537,-4.624],[43.161,-0.001]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.913999968884,0.552999997606,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[43.411,4.875],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":10,\"ty\":4,\"nm\":\"Layer 12 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[80,71.304,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[38.794,39.028,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[2.081,15.465],[-8.926,-0.001],[0,0],[-0.012,2.167],[0,0],[0.006,0],[-23.652,-22.431],[-5.943,2.144]],\"o\":[[-1.19,-8.846],[0,0],[0.187,-2.873],[0,0],[-0.006,0],[0.09,15.041],[4.82,4.571],[-14.067,-15.618]],\"v\":[[-15.785,-14.447],[-1.206,-31.169],[38.271,-31.169],[38.544,-38.777],[-38.527,-38.777],[-38.544,-38.778],[-11.805,33.001],[5.898,36.634]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.702000038297,0.195999998205,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[38.794,39.028],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":11,\"ty\":4,\"nm\":\"Layer 13 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[79.999,71.527,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[38.794,39.25,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0.007,0],[-23.653,-22.431],[-6.556,6.217],[-0.089,15.042]],\"o\":[[-0.006,0],[0.09,15.042],[6.561,6.222],[23.653,-22.43],[0,0]],\"v\":[[-38.525,-38.999],[-38.544,-39.001],[-11.803,32.779],[11.805,32.779],[38.544,-38.999]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.866999966491,0.250999989229,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[38.794,39.25],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":12,\"ty\":4,\"nm\":\"Layer 14 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[80.209,149.83,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[15.632,5.079,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[3.575,2.031],[4.293,0],[3.483,-1.98],[-4.112,0]],\"o\":[[4.112,0],[-3.484,-1.98],[-4.294,0],[-3.575,2.031],[0,0]],\"v\":[[9.256,4.829],[11.807,-1.719],[0,-4.829],[-11.807,-1.719],[-9.256,4.829]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.866999966491,0.250999989229,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[15.631,5.08],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":13,\"ty\":4,\"nm\":\"Layer 15 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[80,113.063,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[10.142,7.438,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[5.394,3.208],[0,-1.615],[-5.462,0],[0,5.463],[0.686,1.346]],\"o\":[[-0.686,1.346],[0,5.463],[5.463,0],[0,-1.615],[-5.393,3.206]],\"v\":[[-8.816,-7.188],[-9.892,-2.704],[0,7.188],[9.892,-2.704],[8.817,-7.188]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.866999966491,0.250999989229,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[10.142,7.438],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":14,\"ty\":4,\"nm\":\"Layer 16 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[74.146,144.019,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[6.103,10.45,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-3.381,0],[0,0],[0.506,-0.238],[0,0],[0,0]],\"o\":[[0,0],[-0.545,-0.014],[-6.277,2.949],[0,0],[2.917,-1.266]],\"v\":[[5.853,8.23],[5.852,-7.077],[4.256,-6.745],[-5.853,-10.199],[-3.662,10.199]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.569000004787,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[6.103,10.45],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":15,\"ty\":4,\"nm\":\"Layer 17 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[74.059,136.348,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[6.191,18.119,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[1.352,0.693],[2.104,-2.027],[-0.176,-1.636],[0,0],[-3.382,0],[0,0]],\"o\":[[-0.353,5.07],[-1.184,1.14],[0,0],[2.917,-1.265],[0,0],[-1.623,0]],\"v\":[[1.433,-17.87],[-4.176,-6.932],[-5.765,-2.529],[-3.574,17.87],[5.941,15.9],[5.938,-16.783]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.702000038297,0.195999998205,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[6.191,18.119],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":16,\"ty\":4,\"nm\":\"Layer 18 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[85.51,144.015,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[6.445,10.451,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[-2.916,-1.265],[0,0],[0,0.002],[0,-0.002],[2.31,1.138],[0.767,-0.129],[0,0],[-0.23,0]],\"o\":[[0,0],[0.001,-0.001],[-0.001,0.001],[-3.425,5.705],[-0.715,-0.351],[0,0],[0.228,-0.006],[3.382,0.001]],\"v\":[[4.005,10.202],[6.194,-10.197],[6.195,-10.202],[6.193,-10.197],[-3.913,-6.678],[-6.195,-7.021],[-6.195,8.251],[-5.51,8.232]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.702000038297,0.195999998205,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[6.445,10.452],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":17,\"ty\":4,\"nm\":\"Layer 19 Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[85.598,136.348,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[6.532,18.119,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[1.186,1.141],[0.353,5.071],[1.623,0],[0.226,0.016],[0,0],[-0.23,0],[-2.916,-1.265],[0,0]],\"o\":[[-2.104,-2.025],[-1.352,0.694],[-0.231,0],[0,0],[0.228,-0.007],[3.382,0],[0,0],[0.176,-1.635]],\"v\":[[4.517,-6.934],[-1.092,-17.87],[-5.598,-16.782],[-6.283,-16.807],[-6.283,15.919],[-5.598,15.902],[3.917,17.87],[6.107,-2.529]],\"c\":true},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.866999966491,0.250999989229,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[6.532,18.119],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":240,\"st\":0,\"bm\":0}]},{\"id\":\"comp_3\",\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"Shape Layer 1\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":8.008,\"s\":[100]},{\"t\":102.102102102102,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":8.008,\"s\":[400,400,0],\"to\":[1.833,-220.667,0],\"ti\":[85.167,-23.333,0]},{\"t\":86.0860860860861,\"s\":[71,152,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.901960784314,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":8.00800800800801,\"op\":728.728728728729,\"st\":8.00800800800801,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"Shape Layer 2\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":6.006,\"s\":[100]},{\"t\":100.1001001001,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":6.006,\"s\":[400,400,0],\"to\":[101.833,91.333,0],\"ti\":[27.167,-153.333,0]},{\"t\":84.0840840840841,\"s\":[579,746,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.392156869173,0.192156866193,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":6.00600600600601,\"op\":726.726726726727,\"st\":6.00600600600601,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"Shape Layer 3\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":4.004,\"s\":[100]},{\"t\":98.0980980980981,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":4.004,\"s\":[400,400,0],\"to\":[-84.167,-74.667,0],\"ti\":[-88.833,-45.333,0]},{\"t\":82.0820820820821,\"s\":[503,238,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.392156869173,0.192156866193,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":4.004004004004,\"op\":724.724724724725,\"st\":4.004004004004,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"Shape Layer 4\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":2.002,\"s\":[100]},{\"t\":96.0960960960961,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":2.002,\"s\":[400,400,0],\"to\":[-82.167,-112.667,0],\"ti\":[73.167,-199.333,0]},{\"t\":80.0800800800801,\"s\":[91,556,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.392156869173,0.192156866193,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":2.002002002002,\"op\":722.722722722723,\"st\":2.002002002002,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":4,\"nm\":\"Shape Layer 5\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":0,\"s\":[100]},{\"t\":94.0940940940941,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":0,\"s\":[400,400,0],\"to\":[77.833,91.333,0],\"ti\":[51.167,60.667,0]},{\"t\":78.0780780780781,\"s\":[511,308,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.007843137719,0.254901975393,0.501960813999,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":0,\"op\":720.720720720721,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"Shape Layer 6\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":2.002,\"s\":[100]},{\"t\":96.0960960960961,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":2.002,\"s\":[400,400,0],\"to\":[-30.167,-122.667,0],\"ti\":[95.167,-53.333,0]},{\"t\":80.0800800800801,\"s\":[155,280,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.901960844152,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":2.002002002002,\"op\":722.722722722723,\"st\":2.002002002002,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":4,\"nm\":\"Shape Layer 7\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":4.004,\"s\":[100]},{\"t\":98.0980980980981,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":4.004,\"s\":[400,400,0],\"to\":[143.833,49.333,0],\"ti\":[-58.833,48.667,0]},{\"t\":82.0820820820821,\"s\":[681,388.872,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.392156869173,0.192156866193,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":4.004004004004,\"op\":724.724724724725,\"st\":4.004004004004,\"bm\":0},{\"ddd\":0,\"ind\":8,\"ty\":4,\"nm\":\"Shape Layer 8\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":6.006,\"s\":[100]},{\"t\":100.1001001001,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":6.006,\"s\":[400,400,0],\"to\":[-24.167,-72.667,0],\"ti\":[75.167,-5.333,0]},{\"t\":84.0840840840841,\"s\":[257,284,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.392156869173,0.192156866193,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":6.00600600600601,\"op\":726.726726726727,\"st\":6.00600600600601,\"bm\":0},{\"ddd\":0,\"ind\":9,\"ty\":4,\"nm\":\"Shape Layer 9\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":8.008,\"s\":[100]},{\"t\":102.102102102102,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":8.008,\"s\":[400,400,0],\"to\":[-14.167,31.333,0],\"ti\":[65.167,-13.333,0]},{\"t\":86.0860860860861,\"s\":[301,474,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.392156869173,0.192156866193,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":8.00800800800801,\"op\":728.728728728729,\"st\":8.00800800800801,\"bm\":0},{\"ddd\":0,\"ind\":10,\"ty\":4,\"nm\":\"Shape Layer 10\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":0,\"s\":[100]},{\"t\":94.0940940940941,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":0,\"s\":[400,400,0],\"to\":[-8.167,63.333,0],\"ti\":[-62.833,-29.333,0]},{\"t\":78.0780780780781,\"s\":[499,570,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.007843137719,0.254901975393,0.501960813999,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.007843137719,0.254901975393,0.501960813999,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":0,\"op\":720.720720720721,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":11,\"ty\":4,\"nm\":\"Shape Layer 11\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":2.002,\"s\":[100]},{\"t\":96.0960960960961,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":2.002,\"s\":[400,400,0],\"to\":[81.833,-104.667,0],\"ti\":[-58.833,104.667,0]},{\"t\":80.0800800800801,\"s\":[557,68,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.901960844152,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.901960844152,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":2.002002002002,\"op\":722.722722722723,\"st\":2.002002002002,\"bm\":0},{\"ddd\":0,\"ind\":12,\"ty\":4,\"nm\":\"Shape Layer 12\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":4.004,\"s\":[100]},{\"t\":98.0980980980981,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":4.004,\"s\":[400,400,0],\"to\":[151.833,-46.667,0],\"ti\":[-26.833,72.667,0]},{\"t\":82.0820820820821,\"s\":[715,138,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":4.004004004004,\"op\":724.724724724725,\"st\":4.004004004004,\"bm\":0},{\"ddd\":0,\"ind\":13,\"ty\":4,\"nm\":\"Shape Layer 13\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":6.006,\"s\":[100]},{\"t\":100.1001001001,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":6.006,\"s\":[400,400,0],\"to\":[107.833,89.333,0],\"ti\":[-92.833,-103.333,0]},{\"t\":84.0840840840841,\"s\":[679,664,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":6.00600600600601,\"op\":726.726726726727,\"st\":6.00600600600601,\"bm\":0},{\"ddd\":0,\"ind\":14,\"ty\":4,\"nm\":\"Shape Layer 14\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":8.008,\"s\":[100]},{\"t\":102.102102102102,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":8.008,\"s\":[400,400,0],\"to\":[-36.167,53.333,0],\"ti\":[75.167,-39.333,0]},{\"t\":86.0860860860861,\"s\":[97,686,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":8.00800800800801,\"op\":728.728728728729,\"st\":8.00800800800801,\"bm\":0},{\"ddd\":0,\"ind\":15,\"ty\":4,\"nm\":\"Shape Layer 15\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":10.01,\"s\":[100]},{\"t\":104.104104104104,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":10.01,\"s\":[400,400,0],\"to\":[-148.167,-132.667,0],\"ti\":[61.167,-65.333,0]},{\"t\":88.0880880880881,\"s\":[65,340,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.007843137719,0.254901975393,0.501960813999,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.007843137719,0.254901975393,0.501960813999,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":10.01001001001,\"op\":730.730730730731,\"st\":10.01001001001,\"bm\":0},{\"ddd\":0,\"ind\":16,\"ty\":4,\"nm\":\"Shape Layer 16\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":12.012,\"s\":[100]},{\"t\":106.106106106106,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.004,\"y\":0.691},\"o\":{\"x\":0.014,\"y\":0},\"t\":12.012,\"s\":[400,400,0],\"to\":[-55.38,-168.204,0],\"ti\":[27.03,124.27,0]},{\"t\":90.0900900900901,\"s\":[400,23.613,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.901960844152,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.901960844152,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":12.012012012012,\"op\":732.732732732733,\"st\":12.012012012012,\"bm\":0},{\"ddd\":0,\"ind\":17,\"ty\":4,\"nm\":\"Shape Layer 17\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":14.014,\"s\":[100]},{\"t\":108.108108108108,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":14.014,\"s\":[400,400,0],\"to\":[109.833,69.333,0],\"ti\":[97.167,0.667,0]},{\"t\":92.0920920920921,\"s\":[303,660,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":14.014014014014,\"op\":734.734734734735,\"st\":14.014014014014,\"bm\":0},{\"ddd\":0,\"ind\":18,\"ty\":4,\"nm\":\"Shape Layer 18\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":16.016,\"s\":[100]},{\"t\":110.11011011011,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":16.016,\"s\":[400,400,0],\"to\":[41.833,109.333,0],\"ti\":[-71.833,39.667,0]},{\"t\":94.0940940940941,\"s\":[663,498,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945098042488,0.352941185236,0.141176477075,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":16.016016016016,\"op\":736.736736736737,\"st\":16.016016016016,\"bm\":0},{\"ddd\":0,\"ind\":19,\"ty\":4,\"nm\":\"Shape Layer 19\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":18.018,\"s\":[100]},{\"t\":112.112112112112,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":18.018,\"s\":[400,400,0],\"to\":[-0.167,-158.667,0],\"ti\":[89.167,6.667,0]},{\"t\":96.0960960960961,\"s\":[187,88,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.007843137719,0.254901975393,0.501960813999,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.007843137719,0.254901975393,0.501960813999,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":18.018018018018,\"op\":738.738738738739,\"st\":18.018018018018,\"bm\":0},{\"ddd\":0,\"ind\":20,\"ty\":4,\"nm\":\"Shape Layer 20\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[1],\"y\":[1]},\"o\":{\"x\":[0.01],\"y\":[0]},\"t\":0,\"s\":[100]},{\"t\":94.0940940940941,\"s\":[0]}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0,\"y\":1},\"o\":{\"x\":0.01,\"y\":0},\"t\":0,\"s\":[400,400,0],\"to\":[-0.167,-158.667,0],\"ti\":[-92.833,-103.333,0]},{\"t\":78.0780780780781,\"s\":[621,234,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[16,16,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"sr\",\"sy\":1,\"d\":1,\"pt\":{\"a\":0,\"k\":5,\"ix\":3},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":4},\"r\":{\"a\":0,\"k\":0,\"ix\":5},\"ir\":{\"a\":0,\"k\":50,\"ix\":6},\"is\":{\"a\":0,\"k\":0,\"ix\":8},\"or\":{\"a\":0,\"k\":100,\"ix\":7},\"os\":{\"a\":0,\"k\":0,\"ix\":9},\"ix\":1,\"nm\":\"Polystar Path 1\",\"mn\":\"ADBE Vector Shape - Star\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.901960844152,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,0.901960844152,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"Fill 2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false}],\"ip\":0,\"op\":720.720720720721,\"st\":0,\"bm\":0}]}],\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":0,\"nm\":\"Main\",\"refId\":\"comp_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[360,300,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[360,300,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"w\":720,\"h\":600,\"ip\":0,\"op\":180,\"st\":0,\"bm\":0}],\"markers\":[]}"
  },
  {
    "path": "core/src/main/res/raw/wordle_list.txt",
    "content": "AABBS\nAABFS\nAABGA\nAABPS\nAABTM\nAACAP\nAACBC\nAACBP\nAACDP\nAACFT\nAACHS\nAACJC\nAACOM\nAACOS\nAACPA\nAACPP\nAACPR\nAACPS\nAACSA\nAACSB\nAACSL\nAACSM\nAACSS\nAACTE\nAACTP\nAACUS\nAACVS\nAADAS\nAADCP\nAADCS\nAADGB\nAAFES\nAAHED\nAAION\nAALII\nAALST\nAAMFT\nAAMGA\nAAMOF\nAANYA\nAAPAS\nAAPIS\nAARAH\nAARAU\nAARAV\nAAREN\nAARGH\nAARON\nAAROO\nAARTI\nAASAX\nAASLT\nAATXE\nAAVSO\nABAAS\nABAAT\nABABS\nABACA\nABACI\nABACK\nABACO\nABACS\nABADA\nABADE\nABADS\nABAFT\nABAGA\nABAHT\nABAKA\nABAKO\nABAMP\nABAND\nABARE\nABASE\nABASH\nABASI\nABASK\nABATE\nABAYA\nABAYS\nABAZA\nABAZI\nABBAS\nABBAT\nABBAY\nABBED\nABBES\nABBEY\nABBIE\nABBOT\nABBRS\nABCCC\nABCDE\nABCDS\nABCPS\nABDAL\nABDIS\nABDON\nABDOS\nABDOU\nABDUL\nABEAM\nABEAR\nABEAT\nABEBE\nABEDS\nABEER\nABELE\nABELL\nABEND\nABENG\nABERG\nABERR\nABETS\nABGAR\nABHAL\nABHOR\nABIAH\nABIDE\nABIDS\nABIER\nABIES\nABILA\nABILO\nABIME\nABITE\nABIUS\nABJAD\nABKAR\nABLED\nABLEN\nABLER\nABLES\nABLET\nABLOW\nABLUR\nABMHO\nABNER\nABNET\nABNEY\nABOAT\nABODE\nABOHM\nABOIL\nABOMA\nABOOD\nABOON\nABOOT\nABORD\nABORE\nABORN\nABORS\nABORT\nABOUD\nABOUE\nABOUT\nABOVE\nABOWT\nABRAM\nABRAS\nABRAY\nABREK\nABRIM\nABRIN\nABRIS\nABRON\nABSDS\nABSEY\nABSIS\nABSIT\nABUJA\nABUNA\nABUNE\nABUNG\nABUNS\nABURA\nABURE\nABURN\nABUSE\nABUTS\nABUTT\nABUYS\nABUZZ\nABWAB\nABWES\nABYDE\nABYED\nABYES\nABYME\nABYSM\nABYSS\nACADA\nACAIS\nACANA\nACAPU\nACARA\nACARI\nACARS\nACATS\nACCAD\nACCAS\nACCEL\nACCOY\nACCRA\nACCTS\nACCUS\nACDNR\nACEDO\nACELA\nACENE\nACERB\nACERO\nACERS\nACESO\nACETO\nACHAB\nACHAR\nACHAZ\nACHED\nACHEE\nACHER\nACHES\nACHOO\nACHOR\nACIDS\nACIDY\nACIES\nACING\nACINI\nACISE\nACKEE\nACKER\nACMES\nACMIC\nACNED\nACNES\nACOBA\nACOCK\nACODS\nACOEL\nACOFF\nACOLD\nACOLI\nACOMA\nACORD\nACORN\nACOYS\nACRAL\nACRED\nACREE\nACRES\nACRID\nACROA\nACRON\nACRRS\nACRUE\nACRUX\nACRYL\nACTED\nACTER\nACTIN\nACTOL\nACTON\nACTOR\nACTUS\nACUFF\nACUNA\nACUTE\nACYLS\nADABE\nADAGE\nADAIR\nADAMS\nADANA\nADANG\nADAPT\nADARS\nADATS\nADAWS\nADAYS\nADBOT\nADCOL\nADCOX\nADDAS\nADDAX\nADDED\nADDER\nADDES\nADDEY\nADDIE\nADDIS\nADDLE\nADDLS\nADDON\nADDRA\nADEBS\nADEEM\nADELA\nADELE\nADELL\nADENI\nADENO\nADEPS\nADEPT\nADFIX\nADGER\nADHAN\nADHIV\nADHYA\nADIEU\nADIGE\nADIOS\nADIRE\nADITI\nADITS\nADIVE\nADJEI\nADJPS\nADKIN\nADLAI\nADLAY\nADLER\nADLEY\nADLIB\nADMAG\nADMAN\nADMEN\nADMES\nADMET\nADMIN\nADMIT\nADMIX\nADNAN\nADNAS\nADNEY\nADOBE\nADOBO\nADOLF\nADOPT\nADORB\nADORE\nADORN\nADOWN\nADOZE\nADPLL\nADRAW\nADREE\nADRET\nADREW\nADRIA\nADRIC\nADRIP\nADRTS\nADSCS\nADSIT\nADSLS\nADUFE\nADUGE\nADULT\nADUNC\nADURE\nADUST\nADVIL\nADVPS\nADVTS\nADYTA\nADYTS\nADZED\nADZES\nAEAEA\nAECIA\nAEDES\nAEDST\nAEGEN\nAEGER\nAEGID\nAEGIR\nAEGIS\nAEGLE\nAEMTS\nAEONS\nAERIE\nAEROS\nAERYN\nAESAH\nAESIR\nAESOP\nAETAS\nAETAT\nAETNA\nAETTS\nAEVUM\nAFACE\nAFADE\nAFAIC\nAFAIK\nAFAIR\nAFAIU\nAFARA\nAFARE\nAFDBS\nAFEAR\nAFERS\nAFFIX\nAFFOR\nAFGOD\nAFIBS\nAFIRE\nAFLAJ\nAFLAO\nAFLAT\nAFLCA\nAFLOP\nAFLOW\nAFLPA\nAFLUA\nAFOAM\nAFOLS\nAFOOR\nAFOOT\nAFORE\nAFOUL\nAFOXE\nAFPAK\nAFPER\nAFQTS\nAFRIC\nAFRIT\nAFRMS\nAFROS\nAFSMS\nAFTAH\nAFTER\nAFTON\nAFTRE\nAFTUH\nAFULA\nAGAIN\nAGAIT\nAGALS\nAGAMA\nAGAMI\nAGAMY\nAGANA\nAGANE\nAGANS\nAGAPE\nAGARD\nAGARS\nAGASP\nAGAST\nAGATE\nAGATY\nAGAVE\nAGAYN\nAGAZE\nAGCAS\nAGEAN\nAGEES\nAGEND\nAGENE\nAGENT\nAGERS\nAGEST\nAGETH\nAGGAT\nAGGER\nAGGIE\nAGGRI\nAGGRO\nAGGRY\nAGHAS\nAGHUL\nAGIDA\nAGILE\nAGING\nAGINS\nAGIOS\nAGISM\nAGIST\nAGITA\nAGITO\nAGLEE\nAGLER\nAGLET\nAGLEY\nAGLOO\nAGLOW\nAGMAS\nAGNEL\nAGNER\nAGNES\nAGNEW\nAGOES\nAGOGE\nAGOGO\nAGONE\nAGONS\nAGONY\nAGOOD\nAGORA\nAGREE\nAGRIN\nAGROM\nAGSMS\nAGTAS\nAGUED\nAGUES\nAGUEY\nAGULY\nAGUON\nAGUSH\nAGUST\nAGYEN\nAHART\nAHATA\nAHAUS\nAHEAD\nAHEAP\nAHEMS\nAHERN\nAHHED\nAHIGH\nAHIND\nAHING\nAHINT\nAHLIE\nAHMAD\nAHMED\nAHMIR\nAHNER\nAHOGE\nAHOLD\nAHOLE\nAHOOL\nAHOYS\nAHRSS\nAHSAN\nAHTNA\nAHUJA\nAHULL\nAHURA\nAHVAZ\nAHWAI\nAICHI\nAIDAN\nAIDED\nAIDEN\nAIDER\nAIDES\nAIDID\nAIDOS\nAIDYN\nAIEEE\nAIERY\nAIFVS\nAIGER\nAIGHT\nAIGIO\nAIGRE\nAIKED\nAIKEN\nAIKIN\nAILED\nAILER\nAILLT\nAIMED\nAIMEE\nAIMER\nAIMES\nAINEC\nAIOLI\nAIPAC\nAIRAG\nAIRAI\nAIRAN\nAIRDS\nAIRED\nAIRER\nAIRES\nAIRLY\nAIRNA\nAIROL\nAIRTH\nAIRTS\nAISHA\nAISLE\nAISNE\nAISPS\nAITCH\nAITNE\nAITUS\nAITYD\nAIWOO\nAIYEE\nAJAKS\nAJARA\nAJARS\nAJAVA\nAJAWS\nAJAYI\nAJIKA\nAJISH\nAJIVA\nAJMAN\nAJMER\nAJUGA\nAJVAR\nAJWAN\nAKABA\nAKANA\nAKANE\nAKARA\nAKASA\nAKAZU\nAKBAR\nAKCES\nAKCHE\nAKEES\nAKELA\nAKEMI\nAKENE\nAKENI\nAKENS\nAKEPA\nAKERS\nAKEYS\nAKHET\nAKIBA\nAKIKO\nAKILL\nAKING\nAKINS\nAKIRA\nAKITA\nAKIVA\nAKKAD\nAKKUB\nAKLAN\nAKNEE\nAKNOW\nAKOLI\nAKPAN\nAKPES\nAKRON\nAKSED\nAKTAU\nAKTER\nAKULE\nAKUMS\nAKURI\nALAAP\nALABI\nALACK\nALAIA\nALALA\nALAMO\nALAMS\nALANA\nALAND\nALANE\nALANG\nALANI\nALANS\nALANT\nALAPA\nALAPH\nALAPS\nALARA\nALARM\nALARP\nALARY\nALATE\nALAVA\nALAVI\nALAWI\nALAYA\nALBAN\nALBAS\nALBAY\nALBED\nALBEE\nALBER\nALBES\nALBIC\nALBIE\nALBIN\nALBMS\nALBOR\nALBOS\nALBRO\nALBUM\nALBYN\nALCAN\nALCID\nALCIE\nALCON\nALCOS\nALDAY\nALDAZ\nALDEN\nALDER\nALDOL\nALEAH\nALEAK\nALECK\nALECS\nALECY\nALEFS\nALEFT\nALEJO\nALEMU\nALEPH\nALERT\nALESI\nALETE\nALEUT\nALEWS\nALEXA\nALFAS\nALFET\nALFIE\nALFIN\nALFIZ\nALGAE\nALGAL\nALGER\nALGIC\nALGID\nALGIN\nALGOL\nALGOS\nALGUM\nALIAS\nALIBI\nALICE\nALIEF\nALIEN\nALIFE\nALIFF\nALIFS\nALIGN\nALIKE\nALIMS\nALINA\nALINE\nALIPS\nALIRE\nALISH\nALIST\nALITA\nALIUE\nALIVE\nALIYA\nALIZA\nALKIE\nALKIN\nALKYD\nALKYL\nALLAH\nALLAN\nALLAT\nALLAY\nALLEE\nALLEN\nALLEX\nALLEY\nALLIE\nALLLS\nALLOA\nALLOD\nALLOO\nALLOT\nALLOW\nALLOY\nALLUS\nALLUZ\nALLYL\nALLYN\nALMAH\nALMAS\nALMEH\nALMER\nALMES\nALMID\nALMON\nALMRY\nALMUD\nALMUG\nALMYS\nALNUM\nALODS\nALOED\nALOES\nALOFT\nALOGI\nALOGY\nALOHA\nALOIN\nALOJA\nALONE\nALONG\nALOOF\nALOOS\nALORA\nALOSA\nALOSE\nALOUD\nALPAC\nALPEN\nALPER\nALPHA\nALRED\nALSEA\nALSIP\nALSOP\nALSUP\nALTAI\nALTAR\nALTAY\nALTER\nALTGR\nALTHO\nALTIE\nALTOM\nALTON\nALTOS\nALTRE\nALTUS\nALTYN\nALULA\nALUMN\nALUMS\nALUMU\nALUNE\nALURE\nALUWA\nALVAR\nALVEI\nALVEN\nALVEY\nALVIA\nALVIN\nALVIS\nALVYN\nALWAY\nALWIN\nALWUZ\nALWYN\nAMADO\nAMAHS\nAMAIN\nAMALA\nAMAND\nAMANN\nAMANO\nAMAPA\nAMARA\nAMARE\nAMARI\nAMARO\nAMARS\nAMARU\nAMASI\nAMASS\nAMATA\nAMATE\nAMATI\nAMAYA\nAMAYS\nAMAZE\nAMBAI\nAMBAL\nAMBAN\nAMBAR\nAMBER\nAMBIT\nAMBIX\nAMBLE\nAMBON\nAMBOS\nAMBOT\nAMBRY\nAMCIS\nAMCIT\nAMDRS\nAMEBA\nAMEEN\nAMEER\nAMEES\nAMELE\nAMELI\nAMELL\nAMELS\nAMEND\nAMENE\nAMENS\nAMENT\nAMEPD\nAMERO\nAMESE\nAMESS\nAMGOT\nAMIAS\nAMICE\nAMICI\nAMICK\nAMICO\nAMIDA\nAMIDE\nAMIDO\nAMIGA\nAMIGO\nAMIID\nAMIKA\nAMINA\nAMIND\nAMINE\nAMINI\nAMINO\nAMIRA\nAMIRI\nAMIRS\nAMISH\nAMISK\nAMISS\nAMITE\nAMITS\nAMITY\nAMIYA\nAMLAH\nAMLAS\nAMLCD\nAMMAN\nAMMAR\nAMMAS\nAMMER\nAMMON\nAMMOS\nAMNIA\nAMNIO\nAMOAH\nAMOCK\nAMOGS\nAMOKS\nAMOLE\nAMONG\nAMORC\nAMORS\nAMORT\nAMORY\nAMOUR\nAMOVE\nAMPED\nAMPER\nAMPLE\nAMPLY\nAMPUL\nAMPYX\nAMRAM\nAMRIT\nAMUCK\nAMURS\nAMUSE\nAMVIS\nAMWFS\nAMYAS\nAMYLE\nAMYLS\nAMYSS\nANAFI\nANAHI\nANALS\nANANA\nANAND\nANAPA\nANATH\nANAYA\nANBAR\nANCAP\nANCHO\nANCLE\nANCON\nANCUS\nANDAI\nANDED\nANDES\nANDIC\nANDOA\nANDRE\nANDRO\nANDRY\nANEAL\nANEAR\nANEJO\nANELE\nANEND\nANENT\nANETA\nANETH\nANGAS\nANGEL\nANGER\nANGIE\nANGIO\nANGLE\nANGLO\nANGON\nANGOR\nANGRY\nANGST\nANGUS\nANHUI\nANIGH\nANILE\nANILS\nANIMA\nANIME\nANIMU\nANION\nANISE\nANITA\nANITO\nANIYA\nANJER\nANJOU\nANJUM\nANKER\nANKHS\nANKIT\nANKLE\nANKOU\nANKUS\nANMOL\nANNAL\nANNAM\nANNAN\nANNAS\nANNAT\nANNEN\nANNES\nANNET\nANNEX\nANNIE\nANNIV\nANNOY\nANNUL\nANNUM\nANOAS\nANODA\nANODE\nANOIA\nANOIL\nANOKA\nANOLE\nANOMY\nANONA\nANONS\nANOON\nANORN\nANOVA\nANSAE\nANSAH\nANSEL\nANSON\nANSUS\nANTAE\nANTED\nANTEM\nANTES\nANTHE\nANTIC\nANTIE\nANTIS\nANTLE\nANTLY\nANTON\nANTRA\nANTRE\nANTSY\nANTWI\nANUAK\nANURY\nANUTA\nANVIL\nANWAR\nANXOM\nANYON\nANYUS\nANZAC\nANZAI\nAOBAO\nAOCRS\nAOEDE\nAOLER\nAOMEN\nAONBS\nAONIA\nAOOGA\nAORTA\nAOSTA\nAOTID\nAOUNS\nAPACE\nAPACS\nAPALA\nAPANS\nAPARS\nAPART\nAPAYS\nAPCBC\nAPCDS\nAPCNR\nAPCRS\nAPDTA\nAPDUS\nAPEAK\nAPEEK\nAPELY\nAPERS\nAPERT\nAPERY\nAPETH\nAPGAR\nAPHID\nAPHIS\nAPIAN\nAPIDS\nAPIIN\nAPINE\nAPING\nAPIOL\nAPIPA\nAPISH\nAPLER\nAPLIN\nAPNEA\nAPODE\nAPODS\nAPOID\nAPOLS\nAPOOP\nAPORT\nAPPAL\nAPPAM\nAPPAR\nAPPAY\nAPPEL\nAPPLE\nAPPLY\nAPPOM\nAPPTD\nAPPTS\nAPPUI\nAPPYS\nAPRES\nAPRIL\nAPRNS\nAPRON\nAPSAR\nAPSED\nAPSES\nAPSIS\nAPTER\nAPTLY\nAPUSH\nAQABA\nAQALS\nAQDAS\nAQMDS\nAQPIK\nAQUAE\nAQUAS\nAQUIC\nAQUIN\nAQUIT\nAQUOD\nAQUOX\nARABA\nARABS\nARABY\nARACA\nARACE\nARACK\nARADS\nARAGE\nARAGH\nARAKI\nARAKS\nARAME\nARANA\nARANS\nARANT\nARAQI\nARARA\nARATA\nARATI\nARAUZ\nARAYA\nARBAS\nARBED\nARBIL\nARBIT\nARBOR\nARCAS\nARCAY\nARCED\nARCEO\nARCHE\nARCHI\nARCHY\nARCIA\nARCIC\nARCID\nARCUS\nARDEB\nARDEN\nARDON\nARDOR\nARDRA\nAREAD\nAREAL\nAREAN\nAREAR\nAREAS\nAREBA\nARECA\nAREEK\nAREET\nAREFY\nAREIC\nARELS\nARELY\nARENA\nARENE\nARENG\nARENT\nAREPA\nARETE\nAREYS\nARFAL\nARFCN\nARFID\nARFIE\nARGAL\nARGAN\nARGIC\nARGID\nARGIE\nARGIL\nARGOL\nARGON\nARGOS\nARGOT\nARGUE\nARGUS\nARHAT\nARHUS\nARIAH\nARIAL\nARIAN\nARIAS\nARICA\nARIEL\nARIES\nARIID\nARILS\nARIMA\nARINJ\nARION\nARIOT\nARISE\nARIST\nARITA\nARITY\nARIUS\nARIYA\nARIZA\nARIZE\nARJUN\nARKAN\nARKIE\nARLES\nARLON\nARLTS\nARMED\nARMER\nARMET\nARMIE\nARMIL\nARMON\nARMOR\nARMTH\nARNAB\nARNAV\nARNDT\nARNEA\nARNER\nARNEY\nARNIE\nARNIS\nARNKS\nARNTZ\nARNUT\nAROAR\nAROBA\nAROHA\nAROID\nAROMA\nAROPA\nAROPH\nARORA\nAROSE\nAROYL\nAROZE\nARPEN\nARPGS\nARPIN\nARRAH\nARRAN\nARRAS\nARRAY\nARRED\nARRER\nARREY\nARRHA\nARRIE\nARRIS\nARRON\nARROW\nARSED\nARSES\nARSEY\nARSIC\nARSIS\nARSLE\nARSON\nARSTE\nARTAB\nARTAL\nARTEL\nARTER\nARTEX\nARTHS\nARTIC\nARTIE\nARTIO\nARTLY\nARTOI\nARTOS\nARTSY\nARTUX\nARUBA\nARUMS\nARUNA\nARURA\nARVAK\nARVAL\nARVEL\nARVID\nARVIE\nARVIL\nARVIN\nARVOS\nARWEL\nARXIV\nARYAN\nARYLS\nARYNE\nASAAD\nASACS\nASADA\nASADI\nASADY\nASAFP\nASAHP\nASAIC\nASAIL\nASAKO\nASAMA\nASANA\nASANO\nASANT\nASAPH\nASARE\nASARO\nASARS\nASATO\nASATS\nASAYS\nASBCA\nASBMS\nASBOS\nASCAP\nASCAR\nASCID\nASCII\nASCOB\nASCON\nASCOT\nASCRA\nASCUS\nASDET\nASDIC\nASEAN\nASEEM\nASEXY\nASFAW\nASHAM\nASHBM\nASHBY\nASHED\nASHEN\nASHER\nASHES\nASHET\nASHIK\nASHIQ\nASHLY\nASHOK\nASHUR\nASIAD\nASIAN\nASIDE\nASILI\nASINE\nASINS\nASITY\nASKAR\nASKED\nASKEE\nASKEN\nASKER\nASKES\nASKEW\nASKEY\nASKIN\nASKOI\nASKOS\nASLUG\nASMAN\nASMAR\nASMRS\nASNAC\nASOAK\nASOKA\nASPAC\nASPCA\nASPDS\nASPEN\nASPER\nASPET\nASPIC\nASPIE\nASPIN\nASPIS\nASPLS\nASPRO\nASRAM\nASRNA\nASROC\nASSAD\nASSAF\nASSAI\nASSAM\nASSAN\nASSAY\nASSEN\nASSES\nASSET\nASSIG\nASSLE\nASSNS\nASSOC\nASSOT\nASSRS\nASSUR\nASTAY\nASTEL\nASTER\nASTIA\nASTIN\nASTIR\nASTLE\nASTMS\nASTON\nASTOR\nASTQB\nASTRO\nASTUN\nASTUR\nASUKA\nASURA\nASVAB\nASWAD\nASWAN\nASWAY\nASWIM\nASYLA\nASYNC\nATAFU\nATAPI\nATARI\nATARS\nATAXY\nATCHA\nATCKS\nATEJI\nATEND\nATENE\nATERS\nATESO\nATEST\nATGAR\nATGMS\nATGWS\nATHAN\nATHAS\nATHEL\nATHEY\nATHOS\nATHYR\nATILT\nATIMY\nATKIN\nATLAS\nATLED\nATLEE\nATMAN\nATMOS\nATNIP\nATOKA\nATOLE\nATOLL\nATOMI\nATOMS\nATOMY\nATONE\nATONG\nATONY\nATOPY\nATRAC\nATRAY\nATREN\nATRIA\nATRIN\nATRIP\nATRYN\nATSIT\nATTAL\nATTAP\nATTAR\nATTER\nATTIA\nATTIC\nATTID\nATTLE\nATTOM\nATTOW\nATTRI\nATTRY\nATWAL\nATWIX\nATYAP\nATYID\nAUASS\nAUBES\nAUBRI\nAUBRY\nAUDAD\nAUDAX\nAUDET\nAUDIE\nAUDIO\nAUDIT\nAUGER\nAUGET\nAUGHT\nAUGIE\nAUGLE\nAUGRE\nAUGUR\nAUKAN\nAUKER\nAULAE\nAULAS\nAULDS\nAULIC\nAULIS\nAULLS\nAULNS\nAULOI\nAULOS\nAULTS\nAUMAN\nAUMIL\nAUNES\nAUNGS\nAUNTS\nAUNTY\nAUOLS\nAURAE\nAURAL\nAURAR\nAURAS\nAUREI\nAURIC\nAURIN\nAURUM\nAUSAS\nAUTEC\nAUTEM\nAUTEN\nAUTHS\nAUTIE\nAUTIN\nAUTOM\nAUTON\nAUTOS\nAUTRY\nAUTUM\nAUVIL\nAUXIC\nAUXIN\nAVAHI\nAVAIL\nAVALE\nAVALS\nAVANT\nAVARS\nAVAST\nAVELS\nAVENS\nAVERA\nAVERI\nAVERS\nAVERT\nAVERY\nAVETA\nAVEYS\nAVGAS\nAVIAN\nAVIEW\nAVILA\nAVILE\nAVINA\nAVISE\nAVISO\nAVIZE\nAVLBS\nAVOID\nAVOKE\nAVORE\nAVOWS\nAVRES\nAVRIL\nAVVID\nAWABI\nAWADH\nAWADS\nAWAIE\nAWAIT\nAWAKE\nAWALT\nAWARA\nAWARD\nAWARE\nAWARI\nAWARN\nAWASH\nAWAVE\nAWAYE\nAWAYS\nAWAZE\nAWDLS\nAWEEL\nAWEIL\nAWENT\nAWFUL\nAWING\nAWKAF\nAWKLY\nAWLET\nAWLUZ\nAWMRY\nAWNED\nAWNER\nAWNGI\nAWNRY\nAWOKE\nAWOLS\nAWOOK\nAWORK\nAWQAF\nAWRAH\nAWRUK\nAWWAD\nAWWED\nAXELS\nAXETH\nAXIAL\nAXIID\nAXILE\nAXILS\nAXING\nAXINO\nAXIOM\nAXION\nAXITE\nAXLED\nAXLES\nAXMAN\nAXMEN\nAXODE\nAXOID\nAXONE\nAXONS\nAXSOM\nAXSON\nAXTON\nAYAAN\nAYADS\nAYAHS\nAYAKA\nAYAKO\nAYALA\nAYAME\nAYAMI\nAYANA\nAYANO\nAYATS\nAYBAR\nAYDEN\nAYDIN\nAYEIN\nAYELE\nAYELP\nAYERS\nAYINS\nAYLET\nAYLLU\nAYLOR\nAYMED\nAYMES\nAYOND\nAYONS\nAYONT\nAYOUB\nAYRAB\nAYRES\nAYRIE\nAYTOS\nAYUBS\nAYUKO\nAYUMI\nAYUSH\nAYUSO\nAYVAR\nAYYAD\nAZAMS\nAZANE\nAZANS\nAZARI\nAZATS\nAZEEZ\nAZENE\nAZERA\nAZERI\nAZETE\nAZIDE\nAZIDO\nAZIMI\nAZINE\nAZINT\nAZIZI\nAZKAL\nAZLON\nAZOIC\nAZOLE\nAZOLO\nAZONE\nAZONS\nAZOTE\nAZOTH\nAZOXY\nAZRAN\nAZTEC\nAZUAS\nAZUAY\nAZUKI\nAZUMI\nAZURE\nAZURN\nAZURY\nAZUSA\nAZYME\nAZZAM\nAZZES\nBAAAD\nBAADE\nBAAED\nBAALS\nBAARS\nBAATH\nBABAR\nBABAS\nBABAX\nBABBS\nBABBY\nBABEE\nBABEH\nBABEL\nBABER\nBABES\nBABEZ\nBABIC\nBABIN\nBABIS\nBABKA\nBABLE\nBABOO\nBABUL\nBABUS\nBACAS\nBACAW\nBACCS\nBACCY\nBACES\nBACHA\nBACHE\nBACHS\nBACKS\nBACKY\nBACNE\nBACON\nBACOO\nBACTA\nBACUP\nBADAL\nBADAM\nBADDY\nBADEN\nBADGE\nBADIA\nBADIU\nBADLA\nBADLY\nBADON\nBADUA\nBADUN\nBAEHR\nBAEKS\nBAELS\nBAENA\nBAEZA\nBAFCS\nBAFFS\nBAFFY\nBAFTA\nBAFTS\nBAFUT\nBAGAD\nBAGAN\nBAGBY\nBAGEL\nBAGES\nBAGGS\nBAGGY\nBAGHS\nBAGLO\nBAGSY\nBAGUA\nBAGUE\nBAHAI\nBAHAM\nBAHAR\nBAHES\nBAHIA\nBAHLS\nBAHMS\nBAHTS\nBAHUS\nBAHUT\nBAIAO\nBAIDU\nBAIER\nBAIGS\nBAIJI\nBAIKS\nBAILA\nBAILE\nBAILS\nBAILY\nBAINE\nBAING\nBAINS\nBAIRD\nBAIRN\nBAIRS\nBAISA\nBAISO\nBAITS\nBAITY\nBAIZA\nBAIZE\nBAJAJ\nBAJAN\nBAJAU\nBAJRA\nBAJRI\nBAJWA\nBAKAR\nBAKAS\nBAKAW\nBAKED\nBAKEN\nBAKER\nBAKES\nBAKKE\nBAKMI\nBAKRI\nBAKSH\nBAKSO\nBALAN\nBALAS\nBALDE\nBALDI\nBALDO\nBALDS\nBALDY\nBALED\nBALER\nBALES\nBALEY\nBALKE\nBALKH\nBALKO\nBALKS\nBALKY\nBALLA\nBALLI\nBALLS\nBALLY\nBALMS\nBALMY\nBALOG\nBALOI\nBALON\nBALOO\nBALOR\nBALOT\nBALSA\nBALTA\nBALTI\nBALTS\nBALTZ\nBALUN\nBALUT\nBALVI\nBAMAR\nBAMAS\nBAMBA\nBAMBI\nBAMES\nBAMFS\nBAMMA\nBAMMO\nBAMMY\nBAMPS\nBAMUM\nBANAL\nBANAN\nBANAT\nBANCK\nBANCO\nBANCS\nBANDA\nBANDH\nBANDI\nBANDO\nBANDS\nBANDY\nBANED\nBANES\nBANEY\nBANFF\nBANGE\nBANGI\nBANGS\nBANGY\nBANHS\nBANHU\nBANIA\nBANIK\nBANJA\nBANJH\nBANJO\nBANKE\nBANKO\nBANKS\nBANKU\nBANKY\nBANNS\nBANOS\nBANTA\nBANTS\nBANTU\nBANTY\nBANTZ\nBANYA\nBAOJI\nBAOZI\nBAPUS\nBARAD\nBARAK\nBARBS\nBARBZ\nBARCA\nBARCO\nBARDO\nBARDS\nBARDY\nBARED\nBAREN\nBARER\nBARES\nBARFI\nBARFS\nBARFY\nBARGA\nBARGE\nBARGO\nBARIA\nBARIC\nBARIL\nBARIS\nBARKE\nBARKS\nBARKY\nBARMS\nBARMY\nBARNO\nBARNS\nBARNY\nBARON\nBAROS\nBAROT\nBARRA\nBARRE\nBARRO\nBARRY\nBARSE\nBARTA\nBARTH\nBARTO\nBARTS\nBARTZ\nBARUA\nBARYE\nBASAA\nBASAL\nBASAN\nBASAS\nBASCO\nBASED\nBASEL\nBASEN\nBASER\nBASES\nBASEY\nBASHA\nBASHO\nBASHY\nBASIC\nBASIJ\nBASIL\nBASIN\nBASIS\nBASKS\nBASKT\nBASLE\nBASON\nBASRA\nBASSA\nBASSE\nBASSI\nBASSO\nBASSY\nBASTA\nBASTE\nBASTO\nBASTS\nBASUS\nBASYE\nBATAB\nBATAK\nBATCH\nBATED\nBATEN\nBATES\nBATEY\nBATFE\nBATHE\nBATHS\nBATHY\nBATIE\nBATIK\nBATIN\nBATIS\nBATNA\nBATON\nBATOR\nBATRA\nBATTA\nBATTE\nBATTS\nBATTU\nBATTY\nBATUM\nBATYS\nBAUDS\nBAUER\nBAUGH\nBAULK\nBAUME\nBAUNO\nBAURE\nBAURS\nBAUZA\nBAVES\nBAVIN\nBAWAN\nBAWAS\nBAWDS\nBAWDY\nBAWKU\nBAWLS\nBAWLY\nBAWNS\nBAWTY\nBAWWS\nBAXAS\nBAXES\nBAYAN\nBAYAS\nBAYED\nBAYER\nBAYES\nBAYEV\nBAYLY\nBAYNE\nBAYOU\nBAYTS\nBAYZE\nBAZAN\nBAZAR\nBAZAS\nBAZES\nBAZIL\nBAZOO\nBAZZA\nBBBBS\nBBBJS\nBBEGS\nBBIAB\nBBIAF\nBBQED\nBBQER\nBBSER\nBBSES\nBCAAS\nBCERS\nBCSMS\nBCVAS\nBDAYS\nBDFLS\nBDNAS\nBDSER\nBEACH\nBEADS\nBEADY\nBEAGS\nBEAHM\nBEAKS\nBEAKY\nBEALE\nBEALL\nBEALS\nBEAMS\nBEAMY\nBEANO\nBEANS\nBEANT\nBEANY\nBEARD\nBEARE\nBEARN\nBEARS\nBEAST\nBEATH\nBEATI\nBEATO\nBEATS\nBEATY\nBEAUS\nBEAUT\nBEAUX\nBEAVE\nBEBAR\nBEBAY\nBEBEE\nBEBEL\nBEBOP\nBECAK\nBECCA\nBECHT\nBECKA\nBECKI\nBECKS\nBECKY\nBECOS\nBECOZ\nBECRY\nBECUE\nBECUT\nBEDAD\nBEDAG\nBEDAW\nBEDDE\nBEDDY\nBEDEL\nBEDES\nBEDEW\nBEDID\nBEDIM\nBEDIP\nBEDIS\nBEDOG\nBEDOY\nBEDYE\nBEEBE\nBEEBS\nBEECH\nBEEDE\nBEEDI\nBEEFS\nBEEFY\nBEEKS\nBEELS\nBEELY\nBEEMS\nBEENA\nBEENE\nBEEPS\nBEERS\nBEERY\nBEEST\nBEETH\nBEETS\nBEEVE\nBEFAL\nBEFEL\nBEFIE\nBEFIT\nBEFLY\nBEFOE\nBEFOG\nBEFOR\nBEFUR\nBEGAB\nBEGAD\nBEGAN\nBEGAR\nBEGAS\nBEGAT\nBEGAY\nBEGEM\nBEGET\nBEGGE\nBEGGS\nBEGIN\nBEGOB\nBEGOD\nBEGOT\nBEGRY\nBEGTI\nBEGUM\nBEGUN\nBEHAD\nBEHAN\nBEHAP\nBEHAR\nBEHAT\nBEHEN\nBEHEW\nBEHLS\nBEHMS\nBEHNS\nBEHRS\nBEHUE\nBEIDA\nBEIER\nBEIGE\nBEIGY\nBEILD\nBEING\nBEJAN\nBEJAR\nBEJEL\nBEKAH\nBEKAS\nBEKEN\nBELAH\nBELAM\nBELAP\nBELAR\nBELAY\nBELCH\nBELEM\nBELEN\nBELEW\nBELID\nBELIE\nBELIN\nBELKS\nBELLA\nBELLE\nBELLI\nBELLO\nBELLS\nBELLY\nBELOW\nBELTS\nBELTZ\nBELUE\nBELYE\nBEMAD\nBEMAN\nBEMAR\nBEMAS\nBEMBA\nBEMBE\nBEMES\nBEMIX\nBEMOL\nBEMOW\nBEMUD\nBENAR\nBENAS\nBENAT\nBENCH\nBENDA\nBENDE\nBENDS\nBENDY\nBENES\nBENET\nBENEW\nBENGA\nBENGE\nBENIM\nBENIN\nBENJI\nBENJY\nBENKE\nBENKO\nBENKS\nBENNA\nBENNE\nBENNI\nBENNO\nBENNS\nBENNU\nBENNY\nBENOS\nBENOW\nBENSE\nBENTO\nBENTS\nBENTY\nBENTZ\nBENUE\nBENXI\nBENZO\nBEOTS\nBEOUR\nBEPAT\nBEPIS\nBERAN\nBERAY\nBERDE\nBEREC\nBEREK\nBERET\nBERGA\nBERGH\nBERGS\nBERHE\nBERKO\nBERKS\nBERME\nBERMS\nBERNE\nBEROB\nBEROE\nBEROS\nBERRA\nBERRY\nBERTH\nBERTI\nBERYL\nBESAN\nBESAT\nBESAW\nBESAY\nBESEE\nBESET\nBESEW\nBESEX\nBESHT\nBESIT\nBESOM\nBESOS\nBESOT\nBESOW\nBESPY\nBESRA\nBESSA\nBESSE\nBESSY\nBESTE\nBESTS\nBETAG\nBETAS\nBETEE\nBETEL\nBETHE\nBETHS\nBETHY\nBETID\nBETIE\nBETOL\nBETOP\nBETOW\nBETSY\nBETTA\nBETTE\nBETTY\nBEVAN\nBEVEL\nBEVER\nBEVIL\nBEVIN\nBEVIS\nBEVOR\nBEVVY\nBEWAG\nBEWAY\nBEWDY\nBEWED\nBEWET\nBEWIG\nBEWIN\nBEWIT\nBEWON\nBEYER\nBEYNG\nBEYTS\nBEZEL\nBEZES\nBEZIL\nBEZZY\nBFAST\nBFILE\nBFODS\nBFPOS\nBHAGA\nBHAJI\nBHAKT\nBHAMO\nBHAND\nBHANG\nBHATT\nBHAVA\nBHEER\nBHILI\nBHIMA\nBHOLA\nBHOYS\nBHUMI\nBHUNA\nBIACH\nBIALY\nBIANS\nBIBBS\nBIBBY\nBIBES\nBIBIS\nBIBLE\nBICCY\nBICEP\nBICES\nBICKS\nBICKY\nBICOL\nBIDAR\nBIDDY\nBIDED\nBIDEN\nBIDER\nBIDES\nBIDET\nBIDIS\nBIDON\nBIEBS\nBIEHL\nBIEHN\nBIELD\nBIENS\nBIERE\nBIERI\nBIERS\nBIERY\nBIFAN\nBIFFO\nBIFFS\nBIFFY\nBIFID\nBIFTA\nBIGAE\nBIGAM\nBIGAS\nBIGBY\nBIGGE\nBIGGS\nBIGGY\nBIGHA\nBIGHT\nBIGLY\nBIGON\nBIGOS\nBIGOT\nBIGUN\nBIHAR\nBIIAB\nBIIIG\nBIJEL\nBIJOU\nBIKED\nBIKER\nBIKES\nBIKIE\nBIKOL\nBILAL\nBILAT\nBILBO\nBILBY\nBILED\nBILEN\nBILES\nBILGE\nBILGY\nBILIN\nBILJE\nBILKS\nBILLI\nBILLS\nBILLY\nBILOS\nBIMAH\nBIMAS\nBIMBO\nBIMMY\nBINAL\nBINDI\nBINDS\nBINDY\nBINER\nBINES\nBINGE\nBINGO\nBINGS\nBINIT\nBINKS\nBINKY\nBINNA\nBINNS\nBINNY\nBINOM\nBINOS\nBINTS\nBIOCH\nBIOGS\nBIOKO\nBIOME\nBIONS\nBIOSE\nBIOTA\nBIOTS\nBIPAS\nBIPED\nBIPOC\nBIPOD\nBIPPY\nBIRBS\nBIRCH\nBIRDE\nBIRDO\nBIRDS\nBIRDY\nBIRGE\nBIRKS\nBIRKY\nBIRLE\nBIRLS\nBIRON\nBIROS\nBIRRA\nBIRRS\nBIRSE\nBIRSY\nBIRTH\nBIRTS\nBISDN\nBISES\nBISET\nBISKI\nBISKS\nBISMS\nBISON\nBITAR\nBITCH\nBITED\nBITEE\nBITER\nBITES\nBITEY\nBITKI\nBITSY\nBITTS\nBITTY\nBIVIA\nBIVIS\nBIVVY\nBIWAS\nBIXBY\nBIXEN\nBIXIE\nBIXIN\nBIZEN\nBIZET\nBIZZO\nBIZZY\nBJORK\nBLAAS\nBLABS\nBLABY\nBLACK\nBLADE\nBLADS\nBLADY\nBLAGG\nBLAGO\nBLAGS\nBLAHA\nBLAHG\nBLAHS\nBLAIN\nBLAIR\nBLAIS\nBLAKE\nBLAME\nBLAMS\nBLANC\nBLAND\nBLANE\nBLANK\nBLANS\nBLAOW\nBLARE\nBLARG\nBLASE\nBLAST\nBLATE\nBLATO\nBLATS\nBLAWG\nBLAYS\nBLAZE\nBLDGS\nBLDSC\nBLEAD\nBLEAH\nBLEAK\nBLEAM\nBLEAR\nBLEAS\nBLEAT\nBLEAU\nBLEBS\nBLECH\nBLECK\nBLEDS\nBLEED\nBLEEN\nBLEEP\nBLEES\nBLEGS\nBLEND\nBLENK\nBLENT\nBLESH\nBLESS\nBLEST\nBLETS\nBLEVE\nBLEWE\nBLEWS\nBLEYS\nBLIAR\nBLICK\nBLIDA\nBLIES\nBLIGE\nBLIKE\nBLIKS\nBLIMP\nBLIMS\nBLIMY\nBLIND\nBLING\nBLINI\nBLINK\nBLINN\nBLINS\nBLINY\nBLIPS\nBLIRT\nBLISK\nBLISS\nBLITE\nBLITS\nBLITZ\nBLIVE\nBLMER\nBLOAK\nBLOAT\nBLOBS\nBLOCH\nBLOCK\nBLOCS\nBLOGS\nBLOHM\nBLOKE\nBLOND\nBLOOD\nBLOOK\nBLOOM\nBLOOP\nBLOPS\nBLORE\nBLORP\nBLORT\nBLOSE\nBLOTE\nBLOTS\nBLOUD\nBLOVE\nBLOWE\nBLOWN\nBLOWS\nBLOWY\nBLOYD\nBLRGS\nBLUBS\nBLUDS\nBLUDY\nBLUED\nBLUEN\nBLUER\nBLUES\nBLUET\nBLUEY\nBLUFF\nBLUME\nBLUNK\nBLUNT\nBLUPS\nBLURB\nBLURP\nBLURR\nBLURS\nBLURT\nBLUSH\nBLUST\nBLUTH\nBLUTO\nBLVDS\nBLYES\nBLYNX\nBLYPE\nBLYTH\nBMEPS\nBMING\nBMOCS\nBMXED\nBMXER\nBMXES\nBNOCS\nBNPER\nBOACO\nBOAKS\nBOALS\nBOANN\nBOARD\nBOARS\nBOART\nBOAST\nBOATE\nBOATS\nBOATY\nBOBAC\nBOBAK\nBOBAR\nBOBAS\nBOBBI\nBOBBS\nBOBBY\nBOBER\nBOBES\nBOBET\nBOBOL\nBOBOS\nBOCAL\nBOCAT\nBOCCA\nBOCCE\nBOCCI\nBOCHE\nBOCKS\nBOCOR\nBODAI\nBODAS\nBODDY\nBODED\nBODEN\nBODES\nBODGE\nBODHI\nBODIC\nBODIE\nBODIG\nBODIN\nBODIS\nBODLE\nBODOS\nBODYE\nBOECK\nBOENS\nBOEPS\nBOERS\nBOESE\nBOETS\nBOFAN\nBOFDS\nBOFFO\nBOFFS\nBOFHS\nBOGAN\nBOGAR\nBOGER\nBOGEY\nBOGGO\nBOGGS\nBOGGY\nBOGIE\nBOGLE\nBOGOF\nBOGON\nBOGOS\nBOGUE\nBOGUS\nBOHAC\nBOHAN\nBOHEA\nBOHIO\nBOHLS\nBOHNS\nBOHOL\nBOHON\nBOHOS\nBOHRA\nBOHRS\nBOIAR\nBOICE\nBOIDS\nBOIKE\nBOILS\nBOINC\nBOINE\nBOING\nBOINK\nBOISE\nBOIST\nBOKED\nBOKEH\nBOKES\nBOKKE\nBOKOR\nBOKOS\nBOLAK\nBOLAN\nBOLAR\nBOLAS\nBOLDO\nBOLDS\nBOLDT\nBOLDU\nBOLEN\nBOLER\nBOLES\nBOLEY\nBOLIN\nBOLIS\nBOLIX\nBOLLS\nBOLLY\nBOLON\nBOLOS\nBOLSA\nBOLTE\nBOLTH\nBOLTI\nBOLTS\nBOLTY\nBOLTZ\nBOLUS\nBOMAN\nBOMAR\nBOMAS\nBOMBA\nBOMBE\nBOMBS\nBOMMY\nBOMOH\nBONAC\nBONAR\nBONCE\nBONCH\nBONDE\nBONDI\nBONDS\nBONDY\nBONED\nBONER\nBONES\nBONET\nBONEY\nBONGE\nBONGO\nBONGS\nBONIN\nBONKS\nBONNE\nBONNY\nBONOS\nBONSU\nBONTS\nBONUS\nBONZA\nBONZE\nBOOBS\nBOOBY\nBOODY\nBOOED\nBOOER\nBOOES\nBOOFS\nBOOFY\nBOOID\nBOOKE\nBOOKS\nBOOKY\nBOOLE\nBOOLS\nBOOLY\nBOOMS\nBOOMY\nBOONE\nBOONG\nBOONK\nBOONS\nBOOPS\nBOORD\nBOORS\nBOORT\nBOORU\nBOOSE\nBOOST\nBOOSY\nBOOTH\nBOOTS\nBOOTY\nBOOYA\nBOOZA\nBOOZE\nBOOZY\nBOPET\nBOPPS\nBOPPY\nBOPUS\nBORAH\nBORAL\nBORAS\nBORAX\nBORCK\nBORDA\nBORDS\nBORED\nBOREE\nBOREK\nBOREL\nBORER\nBORES\nBORGO\nBORGS\nBORIA\nBORIC\nBORID\nBORIS\nBORKO\nBORKS\nBORKY\nBORNA\nBORNE\nBORNS\nBORNT\nBORNU\nBOROK\nBORON\nBOROS\nBORRA\nBORRY\nBORST\nBORTH\nBORTS\nBORTZ\nBORUM\nBORVO\nBORYL\nBORYS\nBOSAK\nBOSAL\nBOSAS\nBOSCO\nBOSCS\nBOSER\nBOSEY\nBOSHA\nBOSKS\nBOSKY\nBOSMA\nBOSNA\nBOSOM\nBOSON\nBOSOX\nBOSRA\nBOSSY\nBOSTS\nBOSUN\nBOTAN\nBOTCH\nBOTEH\nBOTEL\nBOTES\nBOTHA\nBOTHE\nBOTHY\nBOTOS\nBOTOX\nBOTTI\nBOTTO\nBOTTS\nBOTTY\nBOTUS\nBOUCH\nBOUCK\nBOUDS\nBOUGE\nBOUGH\nBOUIE\nBOUKS\nBOULA\nBOULD\nBOULE\nBOULS\nBOULT\nBOUMA\nBOUND\nBOUNS\nBOURD\nBOURG\nBOURI\nBOURN\nBOURS\nBOUSE\nBOUSY\nBOUTS\nBOVAS\nBOVEE\nBOVES\nBOVID\nBOWAB\nBOWED\nBOWEL\nBOWEN\nBOWER\nBOWES\nBOWGE\nBOWIE\nBOWKS\nBOWLE\nBOWLO\nBOWLS\nBOWND\nBOWNE\nBOWNS\nBOWRE\nBOWSE\nBOWSY\nBOXED\nBOXEN\nBOXER\nBOXES\nBOXLA\nBOXTY\nBOYAR\nBOYAU\nBOYCE\nBOYEA\nBOYED\nBOYER\nBOYFS\nBOYKO\nBOYLE\nBOYNE\nBOYOS\nBOYSY\nBOYTE\nBOYTS\nBOYUM\nBOZAL\nBOZAS\nBOZEK\nBOZES\nBOZON\nBOZOS\nBOZZO\nBPAAS\nBRAAI\nBRACC\nBRACE\nBRACH\nBRACK\nBRACT\nBRACY\nBRADS\nBRADT\nBRADY\nBRAES\nBRAGG\nBRAGS\nBRAHM\nBRAHS\nBRAID\nBRAIL\nBRAIN\nBRAIT\nBRAKE\nBRAKY\nBRALY\nBRAMA\nBRAME\nBRAND\nBRANE\nBRANG\nBRANK\nBRANN\nBRANS\nBRANT\nBRARS\nBRASE\nBRASH\nBRASS\nBRAST\nBRATH\nBRATS\nBRATT\nBRAUD\nBRAVA\nBRAVE\nBRAVO\nBRAWL\nBRAWN\nBRAWT\nBRAXY\nBRAYS\nBRAZE\nBRBNS\nBRBPR\nBREAD\nBREAK\nBREAM\nBREAS\nBRECK\nBREDA\nBREDE\nBREED\nBREEN\nBREES\nBREHM\nBREHS\nBREIT\nBREKS\nBREME\nBRENS\nBRENT\nBREON\nBREST\nBRETT\nBRETZ\nBREVE\nBREWS\nBREYS\nBRIAN\nBRIAR\nBRIBE\nBRICE\nBRICK\nBRICS\nBRIDE\nBRIDG\nBRIDI\nBRIEF\nBRIEN\nBRIER\nBRIGS\nBRILL\nBRILS\nBRIMM\nBRIMS\nBRINE\nBRING\nBRINK\nBRINN\nBRINS\nBRINY\nBRION\nBRISE\nBRISK\nBRISS\nBRITS\nBRITT\nBRITZ\nBRIZE\nBRMMM\nBROAD\nBROAS\nBROBS\nBROCH\nBROCK\nBRODA\nBRODE\nBRODT\nBRODY\nBROGS\nBROHA\nBROID\nBROIL\nBROKE\nBROMA\nBROME\nBROMO\nBRONC\nBROND\nBRONJ\nBRONK\nBRONX\nBRONY\nBROOD\nBROOK\nBROOL\nBROOM\nBROON\nBROSE\nBROSS\nBROST\nBROSY\nBROTH\nBROTT\nBROWN\nBROWS\nBROXY\nBRUCE\nBRUCK\nBRUDS\nBRUEN\nBRUGH\nBRUHN\nBRUHS\nBRUIN\nBRUIT\nBRULE\nBRUME\nBRUMM\nBRUNE\nBRUNG\nBRUNI\nBRUNK\nBRUNN\nBRUNO\nBRUNT\nBRUSH\nBRUSK\nBRUSO\nBRUTE\nBRUVS\nBRYAN\nBRYCE\nBRYER\nBRYID\nBRYKS\nBRYNN\nBRYON\nBSAER\nBSCCO\nBSKYB\nBSODS\nBSSID\nBTAIM\nBTECS\nBUALA\nBUANG\nBUATS\nBUAZE\nBUBAL\nBUBAR\nBUBBA\nBUBBE\nBUBBS\nBUBBY\nBUCAK\nBUCCA\nBUCCI\nBUCHT\nBUCHU\nBUCIO\nBUCKO\nBUCKS\nBUCKY\nBUCYS\nBUDAI\nBUDDA\nBUDDE\nBUDDY\nBUDGE\nBUDJU\nBUDKE\nBUELL\nBUELS\nBUENA\nBUERS\nBUESO\nBUFFA\nBUFFO\nBUFFS\nBUFFY\nBUFIS\nBUFOS\nBUGAN\nBUGGS\nBUGGY\nBUGLE\nBUGLY\nBUGSY\nBUGUN\nBUHID\nBUHLS\nBUICE\nBUILD\nBUILT\nBUIST\nBUKER\nBUKOS\nBULAT\nBULAU\nBULBS\nBULBY\nBULGA\nBULGE\nBULGY\nBULKS\nBULKY\nBULLA\nBULLE\nBULLS\nBULLY\nBULSE\nBULTI\nBUMBO\nBUMED\nBUMMY\nBUMPF\nBUMPH\nBUMPS\nBUMPY\nBUNCE\nBUNCH\nBUNCO\nBUNDA\nBUNDH\nBUNDS\nBUNDT\nBUNDU\nBUNDY\nBUNGI\nBUNGO\nBUNGS\nBUNGU\nBUNGY\nBUNIA\nBUNJY\nBUNKO\nBUNKS\nBUNKY\nBUNNS\nBUNNY\nBUNTS\nBUNTY\nBUNUN\nBUNYA\nBUOLT\nBUONO\nBUOYS\nBUPPS\nBURAN\nBURAO\nBURAU\nBURBA\nBURBS\nBURCH\nBURCO\nBURDA\nBURDO\nBURDS\nBUREK\nBUREL\nBUREN\nBURES\nBURET\nBURFI\nBURGE\nBURGH\nBURGO\nBURGS\nBURHS\nBURIN\nBURKA\nBURKE\nBURKS\nBURKY\nBURLS\nBURLY\nBURMA\nBURNE\nBURNS\nBURNT\nBURON\nBUROO\nBUROS\nBUROW\nBURPS\nBURPY\nBURQA\nBURRA\nBURRO\nBURRS\nBURRY\nBURSA\nBURSE\nBURST\nBURTT\nBURUM\nBURYE\nBUSAA\nBUSAN\nBUSAS\nBUSBY\nBUSCO\nBUSED\nBUSES\nBUSEY\nBUSHA\nBUSHI\nBUSHY\nBUSIE\nBUSKE\nBUSKS\nBUSKY\nBUSSU\nBUSSY\nBUSTA\nBUSTO\nBUSTS\nBUSTY\nBUTAS\nBUTCH\nBUTEO\nBUTHS\nBUTLE\nBUTOH\nBUTTE\nBUTTS\nBUTTY\nBUTUT\nBUTYL\nBUUUT\nBUXOM\nBUYED\nBUYEI\nBUYER\nBUYOU\nBUYUP\nBUZAS\nBUZAU\nBUZUQ\nBUZZY\nBWANA\nBWITI\nBWOYS\nBYAMS\nBYARD\nBYBEE\nBYDLO\nBYEFE\nBYERS\nBYGLY\nBYION\nBYKES\nBYLAW\nBYLDE\nBYLER\nBYNOE\nBYNUM\nBYRAM\nBYRES\nBYRGE\nBYRNE\nBYROM\nBYRON\nBYRUM\nBYSSI\nBYTES\nBYUNS\nBYWAY\nBZZED\nBZZZT\nCAABA\nCAAPI\nCABAL\nCABAN\nCABAR\nCABAS\nCABBY\nCABER\nCABES\nCABIN\nCABLE\nCABOB\nCABOC\nCABOT\nCABRA\nCABRE\nCABRI\nCACAO\nCACAS\nCACHE\nCACHO\nCACIK\nCACKS\nCACKY\nCACTI\nCADAM\nCADDO\nCADDR\nCADDY\nCADED\nCADEL\nCADEN\nCADER\nCADES\nCADET\nCADEW\nCADGE\nCADGY\nCADIE\nCADIS\nCADIX\nCADIZ\nCADLE\nCADMS\nCADRE\nCADRS\nCADYS\nCAECA\nCAFES\nCAFFE\nCAFFS\nCAFIZ\nCAFOS\nCAFTA\nCAGED\nCAGER\nCAGES\nCAGEY\nCAGLE\nCAGOT\nCAGOU\nCAGRS\nCAHNS\nCAHOW\nCAHUE\nCAIDS\nCAILS\nCAINE\nCAINS\nCAIRD\nCAIRN\nCAIRO\nCAITO\nCAJON\nCAJUN\nCAKED\nCAKES\nCAKEY\nCAKRA\nCALAS\nCALCS\nCALEB\nCALEY\nCALFS\nCALID\nCALIF\nCALIN\nCALIX\nCALKS\nCALLA\nCALLI\nCALLS\nCALMA\nCALME\nCALMS\nCALMY\nCALNE\nCALOS\nCALPA\nCALPS\nCALUM\nCALVA\nCALVE\nCALYX\nCAMAS\nCAMBA\nCAMBS\nCAMEL\nCAMEO\nCAMES\nCAMIA\nCAMIS\nCAMMY\nCAMOS\nCAMPA\nCAMPI\nCAMPO\nCAMPS\nCAMPY\nCAMRA\nCAMUN\nCAMUS\nCANAL\nCANAR\nCANDI\nCANDO\nCANDY\nCANED\nCANER\nCANES\nCANEX\nCANEZ\nCANGS\nCANID\nCANNA\nCANNE\nCANNS\nCANNY\nCANOD\nCANOE\nCANON\nCANST\nCANTO\nCANTS\nCANTU\nCANTY\nCANUL\nCANUN\nCANUP\nCAOLI\nCAPAC\nCAPAS\nCAPEA\nCAPED\nCAPEL\nCAPEN\nCAPER\nCAPES\nCAPEX\nCAPIZ\nCAPLE\nCAPMS\nCAPOC\nCAPON\nCAPOS\nCAPOT\nCAPPA\nCAPPS\nCAPRI\nCAPTS\nCAPUL\nCAPUT\nCAPYS\nCARAC\nCARAT\nCARAU\nCARBO\nCARBS\nCARBY\nCARDA\nCARDI\nCARDO\nCARDS\nCARDY\nCARED\nCAREN\nCARER\nCARES\nCARET\nCAREX\nCAREY\nCARGO\nCARIA\nCARIB\nCARID\nCARIN\nCARKS\nCARKY\nCARLA\nCARLE\nCARLI\nCARLL\nCARLO\nCARLS\nCARLY\nCARME\nCARNS\nCARNY\nCAROB\nCAROL\nCAROM\nCARON\nCARPI\nCARPO\nCARPS\nCARRA\nCARRI\nCARRS\nCARRY\nCARSE\nCARSO\nCARSY\nCARTA\nCARTE\nCARTS\nCARTY\nCARUS\nCARVE\nCARYN\nCARYS\nCASAL\nCASAS\nCASCO\nCASED\nCASEN\nCASER\nCASES\nCASEY\nCASKS\nCASON\nCASOS\nCASSE\nCASSI\nCASSO\nCASTA\nCASTE\nCASTO\nCASTS\nCASZH\nCATCH\nCATEL\nCATER\nCATES\nCATHI\nCATHS\nCATHY\nCATIA\nCATIE\nCATIO\nCATLY\nCATOE\nCATOM\nCATSO\nCATTE\nCATTY\nCAUKS\nCAULI\nCAULK\nCAULS\nCAUMA\nCAUPS\nCAURI\nCAUSE\nCAVAE\nCAVAL\nCAVAN\nCAVAS\nCAVED\nCAVER\nCAVES\nCAVEY\nCAVIE\nCAVIL\nCAVIN\nCAVOK\nCAVUM\nCAVUS\nCAWED\nCAWKS\nCAWKY\nCAWLS\nCAXON\nCAYCE\nCAYER\nCAYES\nCAYOR\nCAYOS\nCAZAS\nCAZIC\nCAZMA\nCAZZY\nCBARS\nCBIRS\nCBVIR\nCCAFS\nCCDEV\nCCFLS\nCCING\nCCITT\nCCLCM\nCCRCS\nCCSIS\nCCSNE\nCCTLD\nCCTVS\nCCVOS\nCDDRS\nCDERS\nCDING\nCDISC\nCDKIS\nCDNAS\nCDRAM\nCDRES\nCEARA\nCEASE\nCEBID\nCEBIL\nCEBUS\nCECAL\nCECIE\nCECIL\nCECUM\nCEDAC\nCEDAR\nCEDED\nCEDER\nCEDES\nCEDIA\nCEDIS\nCEDRY\nCEEBS\nCEFRL\nCEGEP\nCEIBA\nCEILI\nCEILS\nCEINT\nCEJAS\nCELEB\nCELIA\nCELIS\nCELLA\nCELLI\nCELLO\nCELLS\nCELLY\nCELOM\nCELTA\nCELTS\nCEMIS\nCENAS\nCENES\nCENSE\nCENSI\nCENTO\nCENTS\nCEORL\nCEOSE\nCEPES\nCEPHS\nCEPIN\nCEQLI\nCEQUE\nCERAS\nCERCI\nCERDA\nCERED\nCERES\nCERGE\nCERIA\nCERIC\nCERIN\nCERNA\nCERNY\nCEROC\nCERON\nCEROS\nCERRA\nCERTS\nCERYL\nCERYS\nCESAR\nCESGS\nCESTI\nCESTS\nCETES\nCETIN\nCETPS\nCETUS\nCETYL\nCEUTA\nCEZVE\nCFHQS\nCFLER\nCFLIP\nCFWHS\nCGMPS\nCHAAS\nCHAAT\nCHACE\nCHACK\nCHACO\nCHADD\nCHADS\nCHAES\nCHAFE\nCHAFF\nCHAFT\nCHAGA\nCHAGO\nCHAIM\nCHAIN\nCHAIR\nCHAIS\nCHAIT\nCHAJA\nCHALK\nCHALS\nCHAMA\nCHAMP\nCHAMS\nCHANA\nCHAND\nCHANG\nCHANK\nCHANS\nCHANT\nCHAON\nCHAOS\nCHAPA\nCHAPE\nCHAPS\nCHAPT\nCHAPU\nCHAQU\nCHARA\nCHARD\nCHARE\nCHARK\nCHARM\nCHARR\nCHARS\nCHART\nCHARY\nCHASE\nCHASM\nCHAST\nCHASU\nCHATE\nCHATI\nCHATS\nCHAUN\nCHAUR\nCHAUS\nCHAVE\nCHAVS\nCHAWL\nCHAWS\nCHAYA\nCHAYS\nCHEAM\nCHEAP\nCHEAR\nCHEAS\nCHEAT\nCHEBS\nCHECE\nCHECK\nCHECO\nCHEDI\nCHEEK\nCHEEM\nCHEEP\nCHEER\nCHEEZ\nCHEFS\nCHEIF\nCHEJU\nCHEKA\nCHEKI\nCHEKS\nCHELA\nCHELE\nCHELF\nCHELL\nCHELM\nCHELP\nCHELS\nCHELY\nCHEMO\nCHEMY\nCHENA\nCHENG\nCHENS\nCHERI\nCHERS\nCHERT\nCHERY\nCHESS\nCHEST\nCHETH\nCHETS\nCHEVE\nCHEVS\nCHEVY\nCHEWA\nCHEWN\nCHEWS\nCHEWY\nCHHAY\nCHHIM\nCHHUN\nCHIAN\nCHIAS\nCHIBA\nCHIBI\nCHIBS\nCHICA\nCHICH\nCHICK\nCHICO\nCHICS\nCHIDE\nCHIEF\nCHIEL\nCHIEM\nCHIEN\nCHIFF\nCHIGS\nCHIHS\nCHIKA\nCHILD\nCHILE\nCHILI\nCHILL\nCHIMB\nCHIME\nCHIMO\nCHIMP\nCHINA\nCHINE\nCHING\nCHINK\nCHINN\nCHINO\nCHINS\nCHINT\nCHIOS\nCHIOT\nCHIOU\nCHIPA\nCHIPS\nCHIRK\nCHIRL\nCHIRM\nCHIRO\nCHIRP\nCHIRR\nCHIRU\nCHISM\nCHIST\nCHITA\nCHITS\nCHIUS\nCHIVA\nCHIVE\nCHIVS\nCHIVY\nCHIYO\nCHKPT\nCHLOE\nCHOAD\nCHOAK\nCHOAT\nCHOCK\nCHOCO\nCHOCS\nCHODA\nCHODE\nCHODS\nCHOES\nCHOGM\nCHOIL\nCHOIR\nCHOIS\nCHOKA\nCHOKE\nCHOKO\nCHOKY\nCHOLE\nCHOLI\nCHOLO\nCHOLY\nCHOMO\nCHOMP\nCHONE\nCHONG\nCHONS\nCHOOF\nCHOOK\nCHOOM\nCHOON\nCHOOS\nCHOPE\nCHOPI\nCHOPP\nCHOPS\nCHOPT\nCHORD\nCHORE\nCHORO\nCHORS\nCHOSE\nCHOSS\nCHOTT\nCHOUS\nCHOUT\nCHOUX\nCHOWK\nCHOWS\nCHOYS\nCHRIS\nCHRON\nCHUBA\nCHUBB\nCHUBI\nCHUBS\nCHUCK\nCHUDE\nCHUDS\nCHUDY\nCHUET\nCHUFA\nCHUFF\nCHUGS\nCHUIS\nCHULA\nCHUMP\nCHUMS\nCHUNG\nCHUNI\nCHUNK\nCHUNN\nCHUNS\nCHUPA\nCHUPE\nCHUPP\nCHURI\nCHURL\nCHURN\nCHURR\nCHURS\nCHURU\nCHUSE\nCHUTE\nCHUTS\nCHUUK\nCHYLE\nCHYME\nCHYNA\nCIAAW\nCIANO\nCIAOS\nCIARA\nCIBER\nCIBOA\nCIBOL\nCICHY\nCICUS\nCIDER\nCIELO\nCIELS\nCIFAL\nCIGAR\nCIGGY\nCIHAK\nCIIDS\nCIJIN\nCILIA\nCILLA\nCILLS\nCILLY\nCIMAR\nCIMEX\nCIMID\nCINCA\nCINCH\nCINCO\nCINCS\nCINCT\nCINCY\nCINDI\nCINDY\nCINQS\nCINTI\nCIOID\nCIONS\nCIPPI\nCIPRO\nCIRAS\nCIRCA\nCIRCE\nCIRCS\nCIRES\nCIRLS\nCIRON\nCIRRI\nCISCO\nCISCS\nCISID\nCISPA\nCISSE\nCISSP\nCISSY\nCISTI\nCISTS\nCITAL\nCITED\nCITEH\nCITER\nCITES\nCITIE\nCIVES\nCIVET\nCIVEY\nCIVIC\nCIVIL\nCIVVY\nCIZEK\nCLAAR\nCLACK\nCLADE\nCLADI\nCLADS\nCLAES\nCLAGG\nCLAGS\nCLAIK\nCLAIM\nCLAIR\nCLAIT\nCLAKE\nCLAMP\nCLAMS\nCLANA\nCLANE\nCLANG\nCLANK\nCLANS\nCLAPE\nCLAPP\nCLAPS\nCLAPT\nCLARA\nCLARE\nCLARK\nCLARO\nCLART\nCLARY\nCLASH\nCLASP\nCLASS\nCLAST\nCLAUD\nCLAUS\nCLAUT\nCLAVA\nCLAVE\nCLAVI\nCLAVY\nCLAWS\nCLAWY\nCLAYE\nCLAYM\nCLAYS\nCLDCS\nCLEAN\nCLEAR\nCLEAT\nCLECK\nCLECS\nCLEEK\nCLEEP\nCLEER\nCLEFS\nCLEFT\nCLEGG\nCLEGS\nCLEIT\nCLEMS\nCLEPE\nCLEPT\nCLERK\nCLETA\nCLEVE\nCLEWS\nCLEYS\nCLICK\nCLIED\nCLIES\nCLIFF\nCLIFT\nCLIMB\nCLIME\nCLINE\nCLING\nCLINK\nCLINT\nCLIOS\nCLIPS\nCLIPT\nCLITS\nCLIVE\nCLIVI\nCLOAK\nCLOAM\nCLOBS\nCLOCK\nCLODS\nCLOER\nCLOFF\nCLOGS\nCLOIS\nCLOKE\nCLOMB\nCLOME\nCLOMP\nCLONE\nCLONG\nCLONK\nCLOOM\nCLOOP\nCLOPS\nCLORA\nCLORE\nCLOSE\nCLOSH\nCLOSS\nCLOST\nCLOTE\nCLOTH\nCLOTS\nCLOUD\nCLOUR\nCLOUS\nCLOUT\nCLOVE\nCLOWD\nCLOWN\nCLOWS\nCLOYD\nCLOYS\nCLOZE\nCLSID\nCLUBB\nCLUBS\nCLUCK\nCLUED\nCLUES\nCLUEY\nCLUFF\nCLUMB\nCLUMP\nCLUMS\nCLUNE\nCLUNG\nCLUNK\nCLUON\nCLUTE\nCLUTS\nCLUTZ\nCLYDE\nCLYNE\nCLYPT\nCMAVO\nCMDRE\nCMENE\nCMIIW\nCNATT\nCNDER\nCNIDA\nCOACH\nCOACT\nCOADD\nCOADS\nCOADY\nCOAGS\nCOAKS\nCOALE\nCOALS\nCOALY\nCOANS\nCOAPT\nCOARB\nCOARC\nCOAST\nCOATE\nCOATH\nCOATI\nCOATS\nCOBBS\nCOBBY\nCOBIA\nCOBIE\nCOBLE\nCOBOL\nCOBOS\nCOBOT\nCOBRA\nCOBZA\nCOCAL\nCOCAS\nCOCCI\nCOCCO\nCOCKE\nCOCKS\nCOCKY\nCOCOA\nCOCOS\nCOCOT\nCOCPS\nCOCUS\nCOCUY\nCODAS\nCODDY\nCODEC\nCODED\nCODEL\nCODEN\nCODER\nCODES\nCODEX\nCODGE\nCODGY\nCODLE\nCODON\nCOECA\nCOEDS\nCOEND\nCOEUS\nCOFED\nCOFER\nCOFFA\nCOGAN\nCOGAR\nCOGEN\nCOGER\nCOGON\nCOGUE\nCOHAB\nCOHAN\nCOHEE\nCOHEN\nCOHNS\nCOHOE\nCOHOG\nCOHOS\nCOIFS\nCOIGN\nCOILE\nCOILS\nCOILY\nCOINE\nCOINS\nCOION\nCOIPS\nCOIRS\nCOITS\nCOKED\nCOKER\nCOKES\nCOLAS\nCOLBY\nCOLDE\nCOLDS\nCOLED\nCOLEN\nCOLES\nCOLET\nCOLEY\nCOLIC\nCOLID\nCOLIN\nCOLLA\nCOLLE\nCOLLS\nCOLLY\nCOLMA\nCOLNE\nCOLOG\nCOLON\nCOLOR\nCOLPI\nCOLPS\nCOLTS\nCOLZA\nCOMAE\nCOMAL\nCOMAN\nCOMAR\nCOMAS\nCOMBE\nCOMBI\nCOMBO\nCOMBS\nCOMEN\nCOMER\nCOMES\nCOMET\nCOMEY\nCOMFY\nCOMIC\nCOMIN\nCOMIX\nCOMLY\nCOMMA\nCOMMO\nCOMMS\nCOMMY\nCOMOS\nCOMOX\nCOMPO\nCOMPS\nCOMPT\nCOMPY\nCOMTE\nCOMUS\nCONAL\nCONAN\nCONCH\nCONDE\nCONDO\nCONDS\nCONED\nCONES\nCONEY\nCONFS\nCONGA\nCONGE\nCONGO\nCONGY\nCONIA\nCONIC\nCONID\nCONJS\nCONKS\nCONKY\nCONLY\nCONNS\nCONNY\nCONOR\nCONRY\nCONST\nCONTD\nCONTE\nCONTO\nCONTR\nCONUS\nCONVO\nCONWY\nCOOCH\nCOODY\nCOOED\nCOOEE\nCOOER\nCOOEY\nCOOFS\nCOOJA\nCOOKE\nCOOKS\nCOOKT\nCOOKY\nCOOLE\nCOOLI\nCOOLS\nCOOLY\nCOOMB\nCOOMS\nCOONS\nCOOPS\nCOOPT\nCOORD\nCOORG\nCOORT\nCOOSA\nCOOST\nCOOTS\nCOOTY\nCOOWN\nCOOZE\nCOPAL\nCOPAY\nCOPDS\nCOPED\nCOPEL\nCOPEN\nCOPER\nCOPES\nCOPHA\nCOPPA\nCOPPE\nCOPPS\nCOPPY\nCOPRA\nCOPRO\nCOPSE\nCOPSY\nCOPTS\nCOQUE\nCOQUI\nCORAH\nCORAL\nCORAM\nCORAN\nCORBA\nCORBE\nCORBO\nCORBS\nCORBY\nCORDS\nCORDY\nCOREA\nCORED\nCOREQ\nCORER\nCORES\nCOREY\nCORFE\nCORFS\nCORFU\nCORGI\nCORGY\nCORIA\nCORIN\nCORKS\nCORKY\nCORLE\nCORLS\nCORMI\nCORMO\nCORMS\nCORNA\nCORNO\nCORNS\nCORNU\nCORNY\nCOROL\nCORPS\nCORRO\nCORRS\nCORRY\nCORSE\nCORSI\nCORSO\nCORTE\nCORUH\nCORUM\nCORVE\nCORVI\nCORZO\nCOSBY\nCOSED\nCOSEN\nCOSES\nCOSET\nCOSEY\nCOSHH\nCOSIE\nCOSIO\nCOSMO\nCOSTA\nCOSTE\nCOSTS\nCOTAS\nCOTCH\nCOTED\nCOTES\nCOTHE\nCOTOS\nCOTTA\nCOTTO\nCOTYS\nCOUAS\nCOUCH\nCOUEY\nCOUGH\nCOUIS\nCOULD\nCOULE\nCOUNT\nCOUPE\nCOUPS\nCOURB\nCOURE\nCOURS\nCOURT\nCOURY\nCOUTH\nCOUTO\nCOUTU\nCOVED\nCOVEL\nCOVEN\nCOVER\nCOVES\nCOVET\nCOVEY\nCOVIL\nCOVIN\nCOWAL\nCOWAN\nCOWCH\nCOWED\nCOWEN\nCOWER\nCOWES\nCOWIE\nCOWIN\nCOWLD\nCOWLS\nCOWPS\nCOWRY\nCOXAE\nCOXAL\nCOXED\nCOXES\nCOXIB\nCOXIE\nCOYED\nCOYER\nCOYLE\nCOYLY\nCOYNE\nCOYNS\nCOYPU\nCOZAD\nCOZEN\nCOZIE\nCOZZA\nCOZZI\nCPAPS\nCPCFC\nCPCMS\nCPCTC\nCPDNA\nCPFTS\nCPTPP\nCPUSA\nCRAAL\nCRABB\nCRABS\nCRACE\nCRACK\nCRADY\nCRAFT\nCRAGG\nCRAGO\nCRAGS\nCRAIC\nCRAIG\nCRAIK\nCRAIL\nCRAIN\nCRAKE\nCRALL\nCRAME\nCRAMM\nCRAMP\nCRAMS\nCRANE\nCRANG\nCRANK\nCRANS\nCRANT\nCRAPE\nCRAPO\nCRAPS\nCRAPY\nCRARE\nCRARY\nCRASE\nCRASH\nCRASS\nCRATE\nCRATS\nCRAUN\nCRAVE\nCRAWF\nCRAWK\nCRAWL\nCRAWS\nCRAYE\nCRAYS\nCRAZE\nCRAZY\nCREAK\nCREAM\nCREAR\nCREAS\nCREAT\nCREDO\nCREDS\nCREED\nCREEK\nCREEL\nCREEP\nCREER\nCREES\nCREGO\nCREMA\nCREME\nCREMS\nCRENA\nCREON\nCREOS\nCREPE\nCREPS\nCREPT\nCREPY\nCRESC\nCRESS\nCREST\nCRETE\nCREWE\nCREWS\nCRIAS\nCRIBB\nCRIBO\nCRIBS\nCRICK\nCRIED\nCRIER\nCRIES\nCRIKS\nCRIME\nCRIMI\nCRIMP\nCRIMS\nCRIPE\nCRIPS\nCRISP\nCRITH\nCRITS\nCRIUS\nCRNAC\nCRNAS\nCROAK\nCROAT\nCROCE\nCROCI\nCROCK\nCROCS\nCROFF\nCROFT\nCROKE\nCROLL\nCROMA\nCROME\nCRONE\nCRONK\nCRONS\nCRONY\nCROOK\nCROOL\nCROOM\nCROON\nCROOP\nCROPE\nCROPP\nCROPS\nCRORE\nCROSE\nCROSS\nCROST\nCROUD\nCROUP\nCROUT\nCROWD\nCROWE\nCROWL\nCROWN\nCROWS\nCROYN\nCROYS\nCROZE\nCRPGS\nCRREL\nCRRNA\nCRUCE\nCRUCK\nCRUDE\nCRUDS\nCRUDY\nCRUEL\nCRUES\nCRUET\nCRUFT\nCRULL\nCRUMB\nCRUME\nCRUMP\nCRUMS\nCRUNK\nCRUOR\nCRUPI\nCRUPS\nCRURA\nCRUSE\nCRUSH\nCRUST\nCRUSY\nCRUTE\nCRUTH\nCRUTS\nCRUVE\nCRUZE\nCRWTH\nCRYAN\nCRYER\nCRYIC\nCRYPT\nCSACS\nCSARS\nCSCHE\nCSIBS\nCSNPS\nCSPES\nCSRFS\nCSSOM\nCSTIT\nCTCED\nCTCFS\nCTCRM\nCTCSS\nCTENE\nCTORS\nCTTEE\nCUBAN\nCUBBY\nCUBEB\nCUBED\nCUBER\nCUBES\nCUBIC\nCUBIE\nCUBIT\nCUBOP\nCUCCI\nCUCKS\nCUDDS\nCUDDY\nCUDES\nCUECA\nCUENS\nCUERO\nCUERS\nCUETO\nCUEVA\nCUFFS\nCUFFY\nCUFIC\nCUICA\nCUING\nCUISH\nCUIUI\nCUKES\nCULCH\nCULET\nCULEX\nCULLS\nCULLY\nCULMS\nCULPA\nCULPE\nCULTS\nCULTY\nCUMAE\nCUMAN\nCUMBO\nCUMBY\nCUMEC\nCUMED\nCUMES\nCUMIN\nCUMMY\nCUMYL\nCUNAS\nCUNCH\nCUNDS\nCUNEI\nCUNEO\nCUNGS\nCUNJI\nCUNNY\nCUNTS\nCUNTY\nCUOMO\nCUPAN\nCUPAR\nCUPEL\nCUPID\nCUPIN\nCUPIT\nCUPON\nCUPOS\nCUPPA\nCUPPS\nCUPPY\nCUPRO\nCURAS\nCURAT\nCURBS\nCURBY\nCURCH\nCURCI\nCURDS\nCURDY\nCURED\nCURER\nCURES\nCURET\nCURIA\nCURIE\nCURIO\nCURLI\nCURLS\nCURLY\nCURRS\nCURRY\nCURSE\nCURSI\nCURST\nCURTO\nCURVA\nCURVE\nCURVY\nCUSCO\nCUSEC\nCUSHY\nCUSIC\nCUSKS\nCUSMA\nCUSPS\nCUSPY\nCUSSY\nCUSUM\nCUTAN\nCUTCH\nCUTEN\nCUTER\nCUTES\nCUTEY\nCUTIE\nCUTIN\nCUTIS\nCUTTO\nCUTTY\nCUTUP\nCUVEE\nCUVET\nCUWPL\nCUYAR\nCUZCO\nCUZZA\nCUZZO\nCVADS\nCWMWD\nCWOTS\nCWTCH\nCYANO\nCYANS\nCYBER\nCYBIL\nCYCAD\nCYCAS\nCYCLE\nCYCLI\nCYCLO\nCYDER\nCYENS\nCYLES\nCYLIX\nCYLOR\nCYMAE\nCYMAR\nCYMAS\nCYMES\nCYMOL\nCYMRU\nCYMRY\nCYMWD\nCYNDI\nCYNIC\nCYONS\nCYPIN\nCYRIL\nCYRUS\nCYSER\nCYSTS\nCYTEE\nCYTES\nCYTOL\nCYTON\nCZAJA\nCZARS\nCZECH\nDAADS\nDAALS\nDAANA\nDAASI\nDABAO\nDABBA\nDABBS\nDABCO\nDABOG\nDACCA\nDACES\nDACEY\nDACHA\nDACIA\nDACKS\nDACLS\nDADAS\nDADDA\nDADDY\nDADED\nDADES\nDADLY\nDADOS\nDADYL\nDADYS\nDAEGU\nDAESH\nDAEVA\nDAFFS\nDAFFY\nDAFNI\nDAFNS\nDAFOE\nDAFTY\nDAFUQ\nDAGDA\nDAGEN\nDAGGA\nDAGGE\nDAGGY\nDAGON\nDAGOR\nDAGOS\nDAGUE\nDAGUR\nDAHAL\nDAHAN\nDAHER\nDAHIR\nDAHLE\nDAHLS\nDAHMS\nDAHNS\nDAHUR\nDAHUS\nDAIES\nDAILY\nDAINS\nDAINT\nDAIRY\nDAISE\nDAISH\nDAISY\nDAJID\nDAJRE\nDAKAR\nDAKER\nDAKES\nDAKIN\nDAKIR\nDAKKA\nDALAL\nDALBY\nDALCA\nDALED\nDALEK\nDALEO\nDALES\nDALET\nDALEY\nDALIT\nDALIU\nDALKE\nDALKS\nDALLS\nDALLY\nDALRY\nDALUZ\nDALYN\nDAMAN\nDAMAR\nDAMBO\nDAMEL\nDAMES\nDAMFS\nDAMIR\nDAMMA\nDAMME\nDAMMY\nDAMND\nDAMNS\nDAMON\nDAMPS\nDAMPY\nDANAE\nDANBO\nDANCE\nDANCY\nDANDA\nDANDI\nDANDO\nDANDS\nDANDY\nDANEK\nDANES\nDANFO\nDANGO\nDANGS\nDANHS\nDANIC\nDANIM\nDANIO\nDANIQ\nDANKO\nDANKS\nDANNA\nDANNI\nDANNO\nDANNY\nDANSK\nDANSO\nDANTE\nDANTS\nDANZA\nDANZY\nDAOUD\nDAOWN\nDAPPA\nDARAF\nDARAH\nDARAS\nDARBS\nDARBY\nDARCY\nDARDA\nDARDS\nDARED\nDARER\nDARES\nDARGS\nDARHA\nDARIA\nDARIC\nDARIN\nDARJI\nDARKE\nDARKO\nDARKS\nDARKY\nDARLA\nDARLO\nDARLS\nDARNS\nDARON\nDARPA\nDARRS\nDARST\nDARTH\nDARTS\nDARTY\nDARUG\nDARVO\nDARYL\nDARZI\nDASHI\nDASHT\nDASHY\nDATED\nDATEM\nDATER\nDATES\nDATIL\nDATTA\nDATTO\nDATUM\nDAUBE\nDAUBS\nDAUBY\nDAUER\nDAUGH\nDAULS\nDAUNT\nDAURA\nDAURS\nDAUWS\nDAVAO\nDAVED\nDAVEN\nDAVEY\nDAVID\nDAVIE\nDAVIL\nDAVIN\nDAVIS\nDAVIT\nDAVOR\nDAVOS\nDAVUL\nDAVYS\nDAWAH\nDAWAS\nDAWBS\nDAWDY\nDAWED\nDAWEI\nDAWES\nDAWGS\nDAWKS\nDAWNA\nDAWNS\nDAWRO\nDAWTS\nDAYAK\nDAYAL\nDAYAN\nDAYED\nDAYEE\nDAYER\nDAYES\nDAYGO\nDAYLY\nDAYNT\nDAYSE\nDAYUM\nDAZAS\nDAZED\nDAZEN\nDAZES\nDBAAS\nDBASE\nDBEYR\nDBSES\nDCNNS\nDCXOS\nDDNTP\nDEADE\nDEADS\nDEAFO\nDEAFS\nDEAIR\nDEAKS\nDEALE\nDEALS\nDEALT\nDEALY\nDEANE\nDEANG\nDEANO\nDEANS\nDEARE\nDEARM\nDEARN\nDEARS\nDEARY\nDEASH\nDEASS\nDEATH\nDEBAG\nDEBAR\nDEBBY\nDEBEL\nDEBEN\nDEBIT\nDEBOS\nDEBRA\nDEBTS\nDEBUG\nDEBUS\nDEBUT\nDEBYE\nDECAD\nDECAF\nDECAL\nDECAN\nDECAR\nDECAY\nDECEL\nDECET\nDECIC\nDECIM\nDECKO\nDECKS\nDECON\nDECOR\nDECOY\nDECRY\nDECYL\nDEDAL\nDEEBS\nDEECH\nDEEDE\nDEEDS\nDEEDY\nDEEIN\nDEEKS\nDEEKY\nDEELS\nDEEME\nDEEMS\nDEENA\nDEEPE\nDEEPS\nDEERE\nDEERS\nDEESE\nDEESS\nDEEST\nDEETS\nDEEVS\nDEFAT\nDEFER\nDEFFO\nDEFIB\nDEFIX\nDEFLY\nDEFOE\nDEFOG\nDEFRA\nDEGAN\nDEGAR\nDEGAS\nDEGEL\nDEGEN\nDEGRE\nDEGUM\nDEGUS\nDEGUT\nDEHEX\nDEHNS\nDEICE\nDEIES\nDEIFY\nDEIGN\nDEINK\nDEISM\nDEIST\nDEITY\nDEITZ\nDEKED\nDEKES\nDEKKO\nDEKLE\nDELAM\nDELAO\nDELAS\nDELAY\nDELED\nDELEO\nDELES\nDELFT\nDELHI\nDELIA\nDELID\nDELIS\nDELKS\nDELLA\nDELLE\nDELLS\nDELMA\nDELOS\nDELPH\nDELPS\nDELTA\nDELTS\nDELVE\nDEMAN\nDEMAO\nDEMAP\nDEMAS\nDEMES\nDEMIC\nDEMIS\nDEMIT\nDEMIX\nDEMOB\nDEMOI\nDEMON\nDEMOS\nDEMUR\nDEMUS\nDEMUX\nDENAR\nDENAS\nDENAY\nDENCH\nDENDY\nDENEB\nDENES\nDENET\nDENGA\nDENIB\nDENIE\nDENIM\nDENIN\nDENIS\nDENNE\nDENNY\nDENOS\nDENSE\nDENTS\nDENYS\nDEOIL\nDEOLS\nDEONS\nDEOPS\nDEOXY\nDEPAZ\nDEPEW\nDEPOK\nDEPOS\nDEPOT\nDEPPS\nDEPTH\nDEPTS\nDEQUE\nDERAT\nDERAY\nDERBY\nDERED\nDEREK\nDERES\nDEREZ\nDERIC\nDERMA\nDERMO\nDERMS\nDERNE\nDERNS\nDERNY\nDEROS\nDERPS\nDERPY\nDERRO\nDERRS\nDERRY\nDERTH\nDERYS\nDESAI\nDESAL\nDESAS\nDESAT\nDESEX\nDESHA\nDESHI\nDESID\nDESIR\nDESIS\nDESKS\nDESMA\nDESMO\nDESTS\nDETAG\nDETAX\nDETER\nDETHS\nDETIN\nDETOX\nDETTE\nDETUR\nDEUCE\nDEUEL\nDEUTY\nDEVAN\nDEVAS\nDEVEL\nDEVER\nDEVEX\nDEVIC\nDEVIL\nDEVIN\nDEVOE\nDEVON\nDEVOS\nDEVOW\nDEWAN\nDEWAR\nDEWAX\nDEWDS\nDEWED\nDEWET\nDEWEY\nDEXES\nDEXEU\nDEYES\nDEYOS\nDFACS\nDFDAU\nDFLED\nDFLER\nDFTBA\nDHAAL\nDHABA\nDHAKA\nDHAKI\nDHAKS\nDHALS\nDHAMI\nDHAPS\nDHARS\nDHIKR\nDHIME\nDHOBI\nDHOBY\nDHOLE\nDHOLS\nDHONI\nDHONY\nDHOOP\nDHOTI\nDHOWS\nDHROP\nDHTML\nDHUHR\nDHUTI\nDIABS\nDIADS\nDIAGS\nDIALS\nDIAMS\nDIANA\nDIAND\nDIANE\nDIANN\nDIARY\nDIAZA\nDIAZO\nDIBBA\nDIBLE\nDICED\nDICER\nDICES\nDICEY\nDICKS\nDICKY\nDICOT\nDICTA\nDICTY\nDIDAL\nDIDDY\nDIDEE\nDIDGE\nDIDIE\nDIDJA\nDIDNA\nDIDNT\nDIDNY\nDIDOS\nDIDST\nDIDYA\nDIDYM\nDIEGO\nDIEHL\nDIELS\nDIEMS\nDIENE\nDIEPS\nDIERI\nDIERS\nDIEST\nDIETH\nDIETS\nDIETZ\nDIFFS\nDIGBY\nDIGGS\nDIGHT\nDIGIS\nDIGIT\nDIGNE\nDIGON\nDIGUE\nDIHEX\nDIION\nDIJET\nDIJON\nDIKED\nDIKER\nDIKES\nDIKKA\nDILAL\nDILAN\nDILDO\nDILFS\nDILLI\nDILLS\nDILLY\nDILSA\nDILSK\nDIMBO\nDIMED\nDIMER\nDIMES\nDIMIT\nDIMLY\nDIMMS\nDIMMY\nDIMPS\nDINAH\nDINAR\nDINDU\nDINED\nDINER\nDINES\nDINGE\nDINGO\nDINGS\nDINGY\nDINHS\nDINIC\nDINKA\nDINKS\nDINKY\nDINNA\nDINOI\nDINOS\nDINTS\nDIOCH\nDIODE\nDIOIC\nDIOID\nDIOLS\nDIONE\nDIOPS\nDIOSE\nDIOTA\nDIOXO\nDIPAS\nDIPHE\nDIPIT\nDIPLE\nDIPOD\nDIPPY\nDIPSE\nDIPSO\nDIPSY\nDIPTE\nDIRAC\nDIRAE\nDIRAM\nDIRCK\nDIRER\nDIRGE\nDIRGY\nDIRHM\nDIRKE\nDIRKS\nDIRTH\nDIRTS\nDIRTY\nDISAD\nDISCO\nDISCS\nDISHY\nDISIR\nDISKS\nDISKY\nDISME\nDISNA\nDISTY\nDITAG\nDITAL\nDITAU\nDITCH\nDITED\nDITES\nDITIN\nDITSY\nDITTI\nDITTO\nDITTY\nDITZY\nDIVAN\nDIVAS\nDIVED\nDIVEL\nDIVER\nDIVES\nDIVET\nDIVEY\nDIVIS\nDIVOS\nDIVOT\nDIVVY\nDIVYA\nDIWAN\nDIXEL\nDIXER\nDIXEY\nDIXID\nDIXIE\nDIXIT\nDIXON\nDIYAS\nDIYAT\nDIYED\nDIYER\nDIYNE\nDIYYA\nDIZEN\nDIZIS\nDIZON\nDIZZY\nDJAHI\nDJAHY\nDJARU\nDJELI\nDJENT\nDJING\nDJINN\nDJINS\nDLBCL\nDLING\nDLLME\nDMACA\nDMAPA\nDMARD\nDMCAS\nDMING\nDNAED\nDNASE\nDNEPR\nDNFTT\nDNIPR\nDNSBL\nDNTPS\nDOABS\nDOAKS\nDOANE\nDOANS\nDOATS\nDOBBS\nDOBBY\nDOBIE\nDOBLA\nDOBLE\nDOBRA\nDOBRO\nDOBYS\nDOCKS\nDOCKY\nDOCOS\nDOCUS\nDODAD\nDODDS\nDODDY\nDODEA\nDODGE\nDODGY\nDODIE\nDODOL\nDODOS\nDOEGE\nDOEKE\nDOEKS\nDOERR\nDOERS\nDOEST\nDOETH\nDOFFS\nDOGAL\nDOGAN\nDOGES\nDOGGE\nDOGGO\nDOGGY\nDOGIE\nDOGIS\nDOGLY\nDOGMA\nDOGME\nDOGON\nDOGRI\nDOGSO\nDOHAN\nDOHCS\nDOHMS\nDOHYO\nDOIDS\nDOILY\nDOING\nDOINK\nDOIRA\nDOITS\nDOJIN\nDOJOS\nDOJRA\nDOKDA\nDOKES\nDOKHA\nDOKKA\nDOKOS\nDOKSA\nDOLAN\nDOLBY\nDOLCE\nDOLDS\nDOLED\nDOLES\nDOLIA\nDOLIN\nDOLLS\nDOLLY\nDOLMA\nDOLOR\nDOLOS\nDOLPH\nDOLTS\nDOLUS\nDOMAL\nDOMAN\nDOMED\nDOMER\nDOMES\nDOMET\nDOMKE\nDOMME\nDOMMY\nDOMPT\nDOMRA\nDOMUS\nDONAS\nDONAT\nDONAX\nDONDA\nDONEE\nDONEK\nDONER\nDONEY\nDONGA\nDONGS\nDONIA\nDONKS\nDONNA\nDONNS\nDONNY\nDONOR\nDONSA\nDONUT\nDOOBS\nDOOBT\nDOODS\nDOODY\nDOODZ\nDOOFI\nDOOFS\nDOOFY\nDOOGH\nDOOKS\nDOOKY\nDOOLE\nDOOLY\nDOOMS\nDOOMY\nDOONA\nDOONE\nDOONS\nDOOPS\nDOORE\nDOORS\nDOOSH\nDOOTH\nDOOTY\nDOOZY\nDOPAC\nDOPED\nDOPER\nDOPES\nDOPEY\nDOPPS\nDORAN\nDOREA\nDOREE\nDOREY\nDORFS\nDORGI\nDORIC\nDORID\nDORIS\nDORKS\nDORKY\nDORMS\nDORMY\nDORNS\nDOROW\nDORPS\nDORRS\nDORSA\nDORSE\nDORTA\nDORTS\nDORTY\nDORUS\nDORYS\nDOSAS\nDOSCH\nDOSED\nDOSEH\nDOSEL\nDOSES\nDOSHA\nDOSHI\nDOSRI\nDOTAL\nDOTAR\nDOTED\nDOTEL\nDOTEN\nDOTER\nDOTES\nDOTID\nDOTTY\nDOTYS\nDOUAI\nDOUAR\nDOUAY\nDOUBS\nDOUBT\nDOUCE\nDOUCS\nDOUDU\nDOUFU\nDOUGH\nDOUGO\nDOUIT\nDOULA\nDOUMA\nDOUMS\nDOUNE\nDOUPE\nDOUPS\nDOURA\nDOURO\nDOUSE\nDOUST\nDOUTH\nDOUTS\nDOVEL\nDOVER\nDOVES\nDOVEY\nDOVRE\nDOWAR\nDOWDS\nDOWDY\nDOWED\nDOWEL\nDOWER\nDOWLE\nDOWNE\nDOWNS\nDOWNY\nDOWPS\nDOWRY\nDOWSE\nDOWST\nDOWTY\nDOXAS\nDOXED\nDOXER\nDOXES\nDOXEY\nDOXIE\nDOYAL\nDOYEN\nDOYLE\nDOYLY\nDOYRA\nDOZED\nDOZEN\nDOZER\nDOZES\nDPCCH\nDPDCH\nDPRFP\nDRABS\nDRACO\nDRAFF\nDRAFT\nDRAGN\nDRAGS\nDRAIL\nDRAIN\nDRAIT\nDRAKE\nDRAMA\nDRAMS\nDRANE\nDRANK\nDRANT\nDRAPA\nDRAPE\nDRAPS\nDRATE\nDRATS\nDRAUG\nDRAVA\nDRAVE\nDRAWE\nDRAWL\nDRAWN\nDRAWS\nDRAYS\nDREAD\nDREAM\nDREAP\nDREAR\nDRECK\nDREDS\nDREED\nDREEP\nDREES\nDREGS\nDREHU\nDREIN\nDRESS\nDREST\nDREUL\nDREVE\nDREYS\nDRIBS\nDRIED\nDRIER\nDRIES\nDRIFT\nDRILL\nDRILY\nDRINA\nDRING\nDRINK\nDRINS\nDRIPS\nDRIPT\nDRITE\nDRITH\nDRIUE\nDRIVE\nDRNIS\nDRNJE\nDROCK\nDROFF\nDROID\nDROIL\nDROLL\nDROME\nDROMI\nDRONA\nDRONE\nDRONY\nDROOB\nDROOG\nDROOL\nDROOP\nDROPS\nDROPT\nDROSS\nDROST\nDROTT\nDROVE\nDROWN\nDROZD\nDRUBS\nDRUGG\nDRUGS\nDRUID\nDRUMM\nDRUMS\nDRUNK\nDRUPE\nDRURY\nDRUSE\nDRUSY\nDRUXY\nDRUZE\nDRUZY\nDRYAD\nDRYAS\nDRYER\nDRYES\nDRYLY\nDRYTE\nDRYTH\nDRYTT\nDSDNA\nDSLAM\nDSLRS\nDSMLT\nDSPHS\nDSRNA\nDSRVS\nDSVPS\nDTIMS\nDTMCS\nDTMFA\nDTORS\nDUADS\nDUALA\nDUALS\nDUANE\nDUANG\nDUANS\nDUARS\nDUATS\nDUBAI\nDUBAY\nDUBBO\nDUBBS\nDUBBY\nDUBES\nDUBEY\nDUBHE\nDUBIN\nDUBKI\nDUBON\nDUBUC\nDUBYA\nDUBYS\nDUCAL\nDUCAS\nDUCAT\nDUCES\nDUCET\nDUCEY\nDUCHY\nDUCKS\nDUCKY\nDUCTS\nDUDDY\nDUDED\nDUDES\nDUDEY\nDUDHI\nDUDOU\nDUDUK\nDUELO\nDUELS\nDUERR\nDUERS\nDUESY\nDUETS\nDUETT\nDUFFS\nDUFFY\nDUFUS\nDUGAN\nDUGAR\nDUGLA\nDUHES\nDUHON\nDUKAS\nDUKED\nDUKES\nDUKUN\nDULAC\nDULAS\nDULAY\nDULCE\nDULEY\nDULIA\nDULIC\nDULID\nDULIN\nDULLS\nDULLY\nDULSE\nDUMAN\nDUMAS\nDUMBO\nDUMBS\nDUMKA\nDUMKE\nDUMKY\nDUMMS\nDUMMY\nDUMPS\nDUMPY\nDUNAL\nDUNAM\nDUNAR\nDUNCE\nDUNCH\nDUNCY\nDUNDY\nDUNED\nDUNES\nDUNEY\nDUNGS\nDUNGY\nDUNIV\nDUNKS\nDUNKY\nDUNNE\nDUNNO\nDUNNY\nDUNSH\nDUNTS\nDUNUM\nDUNUN\nDUNZO\nDUOMI\nDUOMO\nDUONG\nDUPED\nDUPEE\nDUPER\nDUPES\nDUPLA\nDUPLE\nDUPLY\nDUPPY\nDUPRE\nDUPUY\nDUQQA\nDUQUE\nDURAG\nDURAL\nDURAS\nDURED\nDUREN\nDUREX\nDURGA\nDURGY\nDURIF\nDURNS\nDUROC\nDURON\nDUROY\nDURRA\nDURRS\nDURRY\nDURSO\nDURST\nDURTY\nDURUM\nDURZI\nDUSEK\nDUSES\nDUSKS\nDUSKY\nDUSTS\nDUSTY\nDUSUN\nDUTAN\nDUTAR\nDUTCH\nDUTIE\nDUTKO\nDUTRA\nDUTTA\nDUTTY\nDUTYS\nDUVAL\nDUVAS\nDUVEL\nDUVET\nDUWAL\nDUXES\nDUZAN\nDVIGU\nDVRED\nDWAAL\nDWALE\nDWALM\nDWANG\nDWARF\nDWECK\nDWEEB\nDWEET\nDWELL\nDWELT\nDWEMS\nDWERE\nDWINE\nDWIRE\nDWORD\nDWYER\nDXERS\nDXING\nDYADS\nDYAKS\nDYALS\nDYANA\nDYARS\nDYBLE\nDYCHE\nDYDOE\nDYERS\nDYERY\nDYEST\nDYETH\nDYETS\nDYING\nDYKED\nDYKES\nDYKEY\nDYKON\nDYLAN\nDYNAE\nDYNAM\nDYNES\nDYNOS\nDYONS\nDYSON\nDYULA\nDYUTI\nDZHOS\nDZOMO\nDZONG\nDZUDS\nEADDY\nEADIE\nEADYS\nEAFRE\nEAGAN\nEAGAR\nEAGEN\nEAGER\nEAGLE\nEAGRE\nEAKER\nEAKIN\nEAKLE\nEAKLY\nEALES\nEALEY\nEALYS\nEAMES\nEAMON\nEANED\nEARAL\nEARBY\nEARED\nEARES\nEARLE\nEARLS\nEARLY\nEARNS\nEARNT\nEAROM\nEARPS\nEARSH\nEARST\nEARTH\nEASED\nEASEL\nEASEN\nEASER\nEASES\nEASIE\nEASON\nEASTS\nEATED\nEATEN\nEATER\nEATHY\nEATON\nEAUDE\nEAVED\nEAVES\nEBARB\nEBAYS\nEBBED\nEBBER\nEBBIE\nEBCTA\nEBELS\nEBENE\nEBERT\nEBERY\nEBEYE\nEBICS\nEBISU\nEBITA\nEBITS\nEBKAC\nEBLEN\nEBLES\nEBLIS\nEBMER\nEBNER\nEBOLA\nEBONS\nEBONY\nEBOOK\nEBRON\nECADS\nECARD\nECASH\nECCER\nECCHI\nECCHO\nECCLE\nECDIS\nECDLS\nECECS\nECENI\nECFVS\nECHED\nECHES\nECHOE\nECHOI\nECHOS\nECKEL\nECKER\nECKIE\nECKLE\nECLAT\nECLOG\nECLSS\nECONS\nECRUS\nECTAD\nECTAL\nECTOR\nECTWT\nEDDER\nEDDIC\nEDDIE\nEDDOE\nEDDOS\nEDEMA\nEDENS\nEDGAR\nEDGED\nEDGEL\nEDGER\nEDGES\nEDGIE\nEDGIN\nEDICK\nEDICT\nEDIFY\nEDILE\nEDINA\nEDITH\nEDITS\nEDLER\nEDLIN\nEDLYN\nEDMAN\nEDMIN\nEDNEY\nEDNOS\nEDOID\nEDSEL\nEDSON\nEDUCE\nEDUCT\nEDWIN\nEEFER\nEEFIN\nEEFIS\nEEJIT\nEEKED\nEELED\nEELER\nEENSY\nEEPED\nEERIE\nEETCH\nEEVEN\nEEVNS\nEFATE\nEFATO\nEFAWS\nEFFED\nEFFER\nEFFET\nEFFIE\nEFFRA\nEFIGS\nEFIRD\nEFOLD\nEFREN\nEGADS\nEGALL\nEGBOS\nEGEAN\nEGERS\nEGEST\nEGGAH\nEGGAR\nEGGED\nEGGER\nEGGES\nEGGIE\nEGGON\nEGHAM\nEGLES\nEGLIS\nEGLOG\nEGNER\nEGNOR\nEGOIC\nEGOLF\nEGRES\nEGRET\nEGUIA\nEGUSI\nEGYPT\nEHEVI\nEHHED\nEHIME\nEHING\nEHLEN\nEHLER\nEHLES\nEHMAN\nEHMKE\nEHRET\nEHSAN\nEIBAR\nEICKS\nEIDER\nEIDOI\nEIDOS\nEIFEL\nEIGHT\nEIGNE\nEIKON\nEILER\nEIRIE\nEIRUV\nEISBN\nEISCH\nEISEL\nEITEL\nEJECT\nEJIDO\nEKING\nEKIRI\nEKKAS\nEKKER\nEKMAN\nEKOID\nEKOTI\nEKPES\nELAHI\nELAIN\nELAND\nELANS\nELARA\nELATE\nELAYL\nELBAZ\nELBIE\nELBOW\nELCHI\nELCIC\nELDAR\nELDEN\nELDER\nELDON\nELECT\nELEET\nELEGY\nELEMI\nELENA\nELENI\nELEYS\nELFED\nELFEN\nELFIN\nELGAN\nELGAR\nELGIN\nELIAN\nELIAS\nELICK\nELIDA\nELIDE\nELIEL\nELIGA\nELINA\nELINT\nELIOT\nELISA\nELISE\nELITE\nELIZA\nELKES\nELKIN\nELLEN\nELLER\nELLIE\nELLIS\nELMAN\nELMEN\nELMER\nELMID\nELOGE\nELOGY\nELOIN\nELONG\nELOPE\nELORA\nELOYI\nELPEE\nELPIS\nELRIG\nELROD\nELRON\nELROY\nELSAN\nELSEA\nELSEN\nELSER\nELSEY\nELSIE\nELSIN\nELSON\nELSPA\nELTON\nELUDE\nELUTE\nELVAN\nELVEN\nELVER\nELVES\nELVIE\nELVIN\nELVIR\nELVIS\nELYSE\nELZEY\nELZYS\nEMACS\nEMAIL\nEMAKI\nEMALS\nEMBAR\nEMBAY\nEMBED\nEMBER\nEMBLA\nEMBOG\nEMBOW\nEMBOX\nEMBRY\nEMBUE\nEMBUS\nEMCEE\nEMDEN\nEMEAI\nEMEER\nEMEIA\nEMEME\nEMEND\nEMERG\nEMERI\nEMERT\nEMERY\nEMESA\nEMEUS\nEMEWS\nEMGES\nEMIGH\nEMIGS\nEMIKO\nEMILE\nEMILY\nEMIRI\nEMIRP\nEMIRS\nEMITS\nEMLYN\nEMMAS\nEMMEL\nEMMEN\nEMMER\nEMMET\nEMMEW\nEMMIE\nEMMYS\nEMOJI\nEMOND\nEMONG\nEMORY\nEMOTE\nEMOVE\nEMPEY\nEMPTY\nEMTFS\nEMULE\nEMYDS\nENACT\nENALS\nENARM\nENATE\nENCKS\nENCUR\nENDED\nENDER\nENDEW\nENDIF\nENDLY\nENDOS\nENDOW\nENDUE\nENEAS\nENEGO\nENEID\nENEMA\nENEMY\nENETS\nENEWS\nENGEN\nENGES\nENGHS\nENGIN\nENGLE\nENGMA\nENGOS\nENGSH\nENIAC\nENIDS\nENIOY\nENJOY\nENKAI\nENKES\nENLAY\nENLIL\nENLIT\nENLOE\nENLOW\nENMEW\nENMIX\nENNET\nENNEW\nENNIS\nENNOG\nENNUI\nENOCH\nENODE\nENOKI\nENOLA\nENOLS\nENONE\nENORM\nENOSE\nENOYL\nENROL\nENRON\nENSES\nENSET\nENSEW\nENSEY\nENSHI\nENSKY\nENSOR\nENSUE\nENTAD\nENTAL\nENTED\nENTER\nENTIA\nENTRE\nENTRY\nENUFF\nENUMS\nENURE\nENVIE\nENVOI\nENVOY\nENYLS\nENYNE\nENZED\nENZES\nENZYM\nEOFAN\nEOFEN\nEOFFS\nEOFYS\nEOLED\nEOLIA\nEOLIC\nEOLID\nEOLIS\nEOLUS\nEONIA\nEONIC\nEORLS\nEOSIN\nEOTEN\nEOUTE\nEOZOA\nEPACT\nEPCOT\nEPEEN\nEPEES\nEPETO\nEPHAH\nEPHAS\nEPHOD\nEPHOR\nEPHUS\nEPICK\nEPICS\nEPIRB\nEPLER\nEPLEY\nEPOCH\nEPODE\nEPONA\nEPOPT\nEPOXY\nEPPLE\nEPRIX\nEPROM\nEPSIN\nEPSOM\nEPSPS\nEPTFE\nEPUBS\nEQUAL\nEQUES\nEQUID\nEQUIP\nEQUOL\nERAMO\nERASE\nERATH\nERATO\nERAZO\nERBIA\nERBIL\nERBYS\nERDUT\nERECT\nERGAL\nERGED\nERGON\nERGOT\nERHUA\nERHUS\nERICA\nERICH\nERICK\nERICS\nERIDU\nERIKA\nERIKO\nERISA\nERITH\nERLER\nERMIN\nERMIT\nERNAI\nERNAS\nERNED\nERNES\nERNIE\nERNST\nERODE\nEROGE\nEROSE\nERRED\nERRNO\nERROL\nERROR\nERSPS\nERTEL\nERTLE\nERTLS\nERTMS\nERUBS\nERUCA\nERUCT\nERUPT\nERUVS\nERVEN\nERVIL\nERWAU\nERWAY\nERWIN\nERZYA\nESAUL\nESBAT\nESCAE\nESCIN\nESCOS\nESCUE\nESHER\nESHES\nESIAS\nESKAR\nESKER\nESKEW\nESKIE\nESLER\nESLOP\nESMEH\nESNES\nESNET\nESOPS\nESPER\nESPEY\nESPIN\nESPOO\nESPYS\nESRAJ\nESRIN\nESRUM\nESSAS\nESSAY\nESSEN\nESSER\nESSES\nESSEX\nESSIE\nESTEC\nESTEP\nESTER\nESTES\nESTEY\nESTHS\nESTON\nESTOP\nESTRE\nETAAC\nETAGE\nETAIN\nETALE\nETAPE\nETCHI\nETEXT\nETHAL\nETHAN\nETHEA\nETHEL\nETHER\nETHIC\nETHOI\nETHOS\nETHYL\nETHYR\nETLAS\nETNAS\nETOPS\nETSOI\nETTEN\nETTER\nETTIN\nETTLE\nETUDE\nETUIS\nETWEE\nETYID\nETYMA\nETYMS\nETZEL\nEUCHE\nEUCRE\nEUERY\nEUFOD\nEUGHS\nEUILL\nEULAH\nEULAS\nEULER\nEURGH\nEUROS\nEURUS\nEURYS\nEUSSR\nEUTHS\nEVACS\nEVADE\nEVALS\nEVANS\nEVATS\nEVATT\nEVENE\nEVENK\nEVENS\nEVENT\nEVERE\nEVERT\nEVERY\nEVETS\nEVETT\nEVICT\nEVILL\nEVILS\nEVITA\nEVITE\nEVOKE\nEVOLA\nEWALT\nEWART\nEWEDU\nEWELL\nEWERS\nEWERT\nEWERY\nEWING\nEWOKS\nEWOOD\nEWWIE\nEXACT\nEXAFS\nEXALT\nEXAMS\nEXAOP\nEXCEL\nEXCOS\nEXCSC\nEXCUR\nEXDIS\nEXEAT\nEXECS\nEXECT\nEXECX\nEXEME\nEXEPT\nEXERT\nEXFAT\nEXFIL\nEXGOD\nEXHIB\nEXILE\nEXINE\nEXING\nEXIRA\nEXIST\nEXITS\nEXLEY\nEXNER\nEXODE\nEXODY\nEXOME\nEXONS\nEXORD\nEXPAT\nEXPEL\nEXPOS\nEXPWY\nEXPYS\nEXSEC\nEXTGS\nEXTOL\nEXTRA\nEXTRY\nEXUDE\nEXULT\nEXUMA\nEXUMS\nEXURB\nEXXON\nEYAKS\nEYASS\nEYELY\nEYERS\nEYETS\nEYGRE\nEYING\nEYLER\nEYMAN\nEYNON\nEYOTS\nEYRAS\nEYREN\nEYRES\nEYRIE\nEYRIR\nEZAFE\nEZELL\nEZHES\nEZHOU\nEZINE\nEZRIN\nEZZIE\nEZZOS\nFAAAN\nFAANG\nFAANS\nFABBO\nFABBY\nFABER\nFABID\nFABLE\nFABOO\nFABRE\nFABRY\nFACED\nFACEP\nFACER\nFACES\nFACET\nFACEY\nFACIA\nFACIO\nFACKS\nFACPS\nFACTA\nFACTO\nFACTS\nFACTY\nFADAS\nFADDY\nFADED\nFADEL\nFADER\nFADES\nFADGE\nFADOS\nFAENA\nFAERY\nFAFDA\nFAFFS\nFAFFY\nFAFIA\nFAFSA\nFAGAN\nFAGEN\nFAGER\nFAGES\nFAGGS\nFAGGY\nFAGIN\nFAGMO\nFAGOT\nFAGUS\nFAHAM\nFAHEY\nFAHMY\nFAIAL\nFAIFO\nFAILE\nFAILS\nFAINE\nFAINS\nFAINT\nFAIRD\nFAIRE\nFAIRS\nFAIRY\nFAITH\nFAKED\nFAKEN\nFAKER\nFAKES\nFAKEY\nFAKIE\nFAKIR\nFAKON\nFALAJ\nFALBO\nFALCK\nFALDA\nFALKS\nFALLA\nFALLS\nFALSA\nFALSE\nFALSY\nFALTS\nFALUN\nFALVO\nFALWE\nFAMED\nFAMES\nFAMIL\nFANAC\nFANAL\nFANAM\nFANCY\nFANDS\nFANED\nFANES\nFANGA\nFANGO\nFANGS\nFANGY\nFANKS\nFANNE\nFANNS\nFANNY\nFANON\nFANOS\nFANTA\nFANTE\nFANTI\nFANUM\nFAQIH\nFAQIR\nFARAD\nFARAG\nFARAH\nFARAJ\nFARBS\nFARBY\nFARCE\nFARCY\nFARDS\nFARED\nFARER\nFARES\nFARGO\nFARIA\nFARID\nFARIK\nFARIO\nFARKS\nFARLS\nFARMS\nFARMY\nFARRA\nFARRE\nFARRO\nFARRY\nFARSE\nFARSI\nFARTS\nFARTY\nFASDS\nFASEL\nFASIQ\nFASON\nFASPA\nFASTI\nFASTS\nFATAH\nFATAL\nFATAS\nFATED\nFATES\nFATHA\nFATHS\nFATLY\nFATSO\nFATTY\nFATWA\nFAUCH\nFAUGH\nFAULD\nFAULE\nFAULK\nFAULS\nFAULT\nFAUNA\nFAUNE\nFAUNI\nFAUNS\nFAUST\nFAUTH\nFAVAS\nFAVED\nFAVEL\nFAVES\nFAVID\nFAVOR\nFAVUS\nFAWAZ\nFAWCE\nFAWNS\nFAWNY\nFAXED\nFAXER\nFAXES\nFAXON\nFAYAD\nFAYAL\nFAYED\nFAYNE\nFAYRE\nFAYTH\nFAYUM\nFAZED\nFAZES\nFAZIO\nFBIER\nFBSES\nFCEVS\nFEAKS\nFEALS\nFEARD\nFEARE\nFEARN\nFEARS\nFEASE\nFEASS\nFEAST\nFEATS\nFEAZE\nFEBSS\nFECAL\nFECES\nFECHT\nFECKS\nFEDAI\nFEDAK\nFEDAN\nFEDEX\nFEDGE\nFEEBS\nFEEDS\nFEELE\nFEELS\nFEELY\nFEENS\nFEERE\nFEESE\nFEETE\nFEETS\nFEEZE\nFEGAN\nFEHER\nFEHLS\nFEHRS\nFEICK\nFEIGN\nFEIGS\nFEINS\nFEINT\nFEIST\nFEITH\nFEITS\nFEJEE\nFELAN\nFELCH\nFELDT\nFELID\nFELIX\nFELLA\nFELLS\nFELLY\nFELON\nFELTE\nFELTS\nFELTY\nFELTZ\nFEMAL\nFEMES\nFEMME\nFEMMY\nFEMUR\nFENCE\nFENDS\nFENDY\nFENER\nFENGS\nFENGU\nFENIS\nFENKS\nFENNE\nFENNI\nFENNY\nFENTS\nFENYA\nFEODS\nFEOFF\nFEOLA\nFEORM\nFERAL\nFERAS\nFERDS\nFERES\nFERIA\nFERIE\nFERKO\nFERME\nFERMI\nFERMS\nFERNS\nFERNY\nFERPA\nFERRA\nFERRE\nFERRI\nFERRO\nFERRY\nFERYS\nFESSE\nFESTA\nFESTS\nFESTY\nFETAL\nFETAS\nFETCH\nFETED\nFETES\nFETID\nFETII\nFETIS\nFETOR\nFETTA\nFETTY\nFETUS\nFETWA\nFEUAR\nFEUDS\nFEUED\nFEVER\nFEVRE\nFEWEL\nFEWER\nFEWLS\nFEWLY\nFEWTE\nFEYER\nFEYLY\nFEZES\nFFDCA\nFHLBB\nFIARS\nFIATS\nFIBER\nFIBRE\nFIBRO\nFIBUA\nFICEK\nFICES\nFICHE\nFICHU\nFICID\nFICIN\nFICKE\nFICTS\nFICUS\nFIDAA\nFIDDY\nFIDED\nFIDEL\nFIDEO\nFIDES\nFIDGE\nFIDOS\nFIEFS\nFIELD\nFIEND\nFIENE\nFIERO\nFIERS\nFIERY\nFIFED\nFIFER\nFIFES\nFIFOS\nFIFTH\nFIFTY\nFIGGS\nFIGGY\nFIGHT\nFIGLU\nFIGMO\nFIGOS\nFIKED\nFIKES\nFILAR\nFILCH\nFILED\nFILER\nFILES\nFILET\nFILKS\nFILLO\nFILLS\nFILLY\nFILMI\nFILMS\nFILMY\nFILOS\nFILST\nFILTH\nFILUM\nFINAL\nFINAN\nFINAU\nFINCA\nFINCH\nFINCK\nFINDE\nFINDS\nFINDY\nFINED\nFINER\nFINES\nFINEW\nFINIF\nFINIS\nFINKE\nFINKS\nFINNA\nFINNS\nFINNY\nFINOS\nFIONA\nFIORD\nFIORI\nFIQUE\nFIRBY\nFIRED\nFIREE\nFIRER\nFIRES\nFIREY\nFIRIE\nFIRKS\nFIRMS\nFIRNS\nFIRRY\nFIRST\nFIRTH\nFISBO\nFISCS\nFISES\nFISHO\nFISHY\nFISKE\nFISKS\nFISTS\nFISTY\nFITBA\nFITCH\nFITES\nFITLY\nFITNA\nFITRA\nFITTE\nFITTS\nFITTY\nFIVED\nFIVER\nFIVES\nFIXED\nFIXEN\nFIXER\nFIXES\nFIXIE\nFIXIT\nFIXLY\nFIXNA\nFIXUP\nFIZBO\nFIZER\nFIZZY\nFJARD\nFJELD\nFJORD\nFKING\nFLABS\nFLACK\nFLAGG\nFLAGS\nFLAIG\nFLAIL\nFLAIM\nFLAIN\nFLAIR\nFLAKE\nFLAKI\nFLAKS\nFLAKY\nFLAME\nFLAMM\nFLAMS\nFLAMY\nFLANG\nFLANK\nFLANS\nFLAPS\nFLAPT\nFLARE\nFLARF\nFLARY\nFLASH\nFLASK\nFLATS\nFLATT\nFLAVA\nFLAWN\nFLAWS\nFLAWY\nFLAXY\nFLAYS\nFLEAD\nFLEAK\nFLEAM\nFLEAS\nFLECK\nFLEDA\nFLEED\nFLEEK\nFLEEN\nFLEER\nFLEES\nFLEET\nFLEHM\nFLEIG\nFLEME\nFLERD\nFLESH\nFLETS\nFLETT\nFLEUR\nFLEVO\nFLEWS\nFLEXO\nFLEXY\nFLEYS\nFLICE\nFLICK\nFLICS\nFLIDS\nFLIED\nFLIER\nFLIES\nFLIFO\nFLIMP\nFLING\nFLINK\nFLINN\nFLINT\nFLIPE\nFLIPS\nFLIPT\nFLIRT\nFLISK\nFLISS\nFLIST\nFLITE\nFLITS\nFLOAT\nFLOBS\nFLOCC\nFLOCK\nFLOCS\nFLODE\nFLOES\nFLOGS\nFLOHR\nFLONE\nFLONG\nFLOOD\nFLOOF\nFLOOK\nFLOOM\nFLOOR\nFLOPS\nFLORA\nFLORS\nFLORY\nFLOSH\nFLOSS\nFLOTA\nFLOTE\nFLOUD\nFLOUR\nFLOUT\nFLOWE\nFLOWK\nFLOWN\nFLOWS\nFLOWY\nFLOYD\nFLUBS\nFLUDD\nFLUED\nFLUES\nFLUEY\nFLUFF\nFLUID\nFLUKE\nFLUKY\nFLUME\nFLUMP\nFLUNG\nFLUNK\nFLUOR\nFLURR\nFLURT\nFLURY\nFLUSH\nFLUTD\nFLUTE\nFLUTY\nFLUYT\nFLYBY\nFLYER\nFLYES\nFLYNN\nFLYNT\nFLYPE\nFLYTE\nFMAIT\nFMOLE\nFMOLS\nFNARR\nFNESE\nFNIRS\nFNORD\nFOAFS\nFOALE\nFOALS\nFOAMS\nFOAMY\nFOARD\nFOBBY\nFOBTS\nFOCAL\nFOCHT\nFOCUS\nFODEN\nFOEHN\nFOETI\nFOGAS\nFOGEL\nFOGES\nFOGEY\nFOGGY\nFOGIE\nFOGLE\nFOGOU\nFOGTS\nFOHES\nFOHNS\nFOIBA\nFOIDS\nFOILS\nFOIND\nFOINE\nFOINS\nFOISM\nFOIST\nFOISY\nFOLAN\nFOLAR\nFOLDS\nFOLEY\nFOLIA\nFOLIC\nFOLIO\nFOLKS\nFOLKY\nFOLLY\nFOLSE\nFOLTZ\nFOLWE\nFOLYL\nFOMBA\nFOMBY\nFOMES\nFONDA\nFONDS\nFONDU\nFONIO\nFONLY\nFONTS\nFOODS\nFOODY\nFOOEY\nFOOFY\nFOOKS\nFOOLE\nFOOLS\nFOONS\nFOORS\nFOOSA\nFOOSE\nFOOTE\nFOOTS\nFOOTY\nFORAM\nFORAN\nFORAY\nFORBS\nFORBY\nFORCE\nFORDE\nFORDO\nFORDS\nFORDY\nFOREL\nFORET\nFOREX\nFORGE\nFORGO\nFORKS\nFORKY\nFORLI\nFORME\nFORML\nFORMS\nFORMY\nFORNE\nFORRO\nFORRY\nFORTE\nFORTH\nFORTI\nFORTS\nFORTY\nFORUM\nFOSHO\nFOSSA\nFOSSE\nFOSSY\nFOSTA\nFOTCH\nFOTHS\nFOTIS\nFOTOG\nFOTOS\nFOUAT\nFOUCH\nFOUDS\nFOUET\nFOUGH\nFOULE\nFOULS\nFOUND\nFOUNT\nFOUPS\nFOURE\nFOURS\nFOUSE\nFOUST\nFOUTA\nFOUTH\nFOUTY\nFOUTZ\nFOVEA\nFOWER\nFOWEY\nFOWLE\nFOWLS\nFOXED\nFOXEN\nFOXER\nFOXES\nFOXIE\nFOXLY\nFOYER\nFOYES\nFOYLE\nFPCON\nFPGAS\nFPSES\nFQDNS\nFQIHS\nFRABS\nFRACK\nFRACS\nFRACT\nFRADY\nFRAGS\nFRAHM\nFRAID\nFRAIL\nFRAIM\nFRAIN\nFRALA\nFRAMA\nFRAME\nFRANC\nFRAND\nFRANK\nFRANS\nFRANZ\nFRAPE\nFRAPS\nFRARY\nFRASE\nFRASS\nFRATE\nFRATI\nFRATS\nFRAUD\nFRAUS\nFRAYS\nFRAZE\nFRCOG\nFRCSS\nFRCVS\nFREAK\nFREAM\nFREAR\nFREAT\nFRECK\nFREDA\nFREDK\nFREDS\nFREED\nFREEL\nFREEP\nFREER\nFREES\nFREET\nFREIA\nFREIT\nFREKE\nFREMD\nFRENA\nFRENS\nFREON\nFREQS\nFRERE\nFRESE\nFRESH\nFRESS\nFRETA\nFRETS\nFRETT\nFRETZ\nFREUD\nFREVO\nFREWS\nFREYA\nFREYR\nFRIAR\nFRIAS\nFRICE\nFRICK\nFRIDA\nFRIED\nFRIEL\nFRIER\nFRIES\nFRIGG\nFRIGO\nFRIGS\nFRILL\nFRINE\nFRINK\nFRISK\nFRIST\nFRITH\nFRITS\nFRITZ\nFRIZE\nFRIZZ\nFROBS\nFROCK\nFRODO\nFROES\nFROGS\nFROIN\nFROLF\nFROME\nFROMM\nFROND\nFRONK\nFRONS\nFRONT\nFROOD\nFROOM\nFRORE\nFRORY\nFROSH\nFROSK\nFROST\nFROTE\nFROTH\nFROTS\nFROUN\nFROUP\nFROWN\nFROWS\nFROWY\nFROYO\nFROZE\nFRUEH\nFRUGE\nFRUGS\nFRUIT\nFRUMP\nFRUNK\nFRUSH\nFRUTH\nFRYAR\nFRYER\nFRYUP\nFSBOS\nFSCKS\nFSECS\nFSING\nFTBFS\nFTIRA\nFTIRS\nFTMFW\nFTPED\nFUAGE\nFUARS\nFUBAR\nFUBBY\nFUBSY\nFUBUS\nFUCAN\nFUCCI\nFUCHS\nFUCKA\nFUCKO\nFUCKS\nFUCKY\nFUCUS\nFUDDS\nFUDGE\nFUDGY\nFUELS\nFUERO\nFUETS\nFUFFS\nFUFFY\nFUGAL\nFUGAS\nFUGGO\nFUGGS\nFUGGY\nFUGIE\nFUGIT\nFUGLE\nFUGLY\nFUGOS\nFUGUE\nFUHRS\nFUJIE\nFUJII\nFUJIN\nFUJIS\nFUKUI\nFULAH\nFULAS\nFULBE\nFULDA\nFULES\nFULKS\nFULLA\nFULLS\nFULLY\nFULLZ\nFULOP\nFULPS\nFULTH\nFULTZ\nFUMED\nFUMER\nFUMES\nFUMET\nFUMID\nFUMIE\nFUNAN\nFUNCT\nFUNDA\nFUNDI\nFUNDS\nFUNDU\nFUNDY\nFUNEN\nFUNEZ\nFUNGE\nFUNGI\nFUNGO\nFUNGS\nFUNIC\nFUNIS\nFUNJI\nFUNKS\nFUNKY\nFUNNY\nFUNTS\nFUOCO\nFUPAS\nFUQUA\nFURAN\nFURBY\nFUREY\nFURIN\nFURLS\nFUROL\nFUROR\nFUROS\nFURPS\nFURRY\nFURST\nFURTH\nFURYL\nFURZE\nFURZY\nFUSCO\nFUSED\nFUSEE\nFUSEN\nFUSER\nFUSES\nFUSIL\nFUSON\nFUSOR\nFUSSY\nFUSTS\nFUSTY\nFUTAS\nFUTCH\nFUTON\nFUZED\nFUZEE\nFUZES\nFUZIL\nFUZZY\nFWDED\nFWEEP\nFWEND\nFWOOM\nFWSAR\nFYALL\nFYFES\nFYFFE\nFYKES\nFYLDE\nFYLES\nFYNDE\nFYNNE\nFYOCK\nFYRDS\nFYRKS\nFYROM\nFYRST\nFYTTE\nGAALS\nGAANG\nGABAE\nGABAR\nGABAY\nGABBA\nGABBY\nGABEL\nGABER\nGABEY\nGABLE\nGABOB\nGABON\nGABOR\nGABRA\nGACEK\nGACHA\nGACHE\nGACKS\nGADAS\nGADDI\nGADDS\nGADDY\nGADEA\nGADES\nGADGE\nGADIC\nGADID\nGADIS\nGADJE\nGADSO\nGAEDE\nGAELS\nGAFFA\nGAFFE\nGAFFS\nGAFIA\nGAGED\nGAGER\nGAGES\nGAGGY\nGAGNE\nGAGRA\nGAHAN\nGAIAN\nGAIDA\nGAIER\nGAIJI\nGAIKA\nGAILY\nGAINS\nGAITS\nGAJDA\nGALAH\nGALAN\nGALAS\nGALAZ\nGALBE\nGALBI\nGALDR\nGALEA\nGALED\nGALEN\nGALES\nGALEX\nGALEY\nGALGE\nGALKA\nGALLO\nGALLS\nGALLY\nGALOP\nGALPS\nGALVO\nGAMAS\nGAMAY\nGAMBA\nGAMBE\nGAMBO\nGAMBS\nGAMED\nGAMEL\nGAMER\nGAMES\nGAMEY\nGAMEZ\nGAMIC\nGAMIE\nGAMIN\nGAMMA\nGAMMY\nGAMON\nGAMPI\nGAMPS\nGAMUT\nGANAI\nGANCA\nGANCH\nGANCI\nGANDA\nGANDY\nGANEF\nGANEV\nGANEY\nGANGA\nGANGE\nGANGI\nGANGL\nGANGS\nGANIF\nGANIL\nGANJA\nGANKS\nGANNA\nGANNS\nGANOE\nGANOF\nGANOS\nGANPI\nGANSA\nGANSU\nGANTA\nGANTE\nGANTT\nGANTZ\nGANZA\nGANZY\nGAOHU\nGAOLS\nGAONA\nGAONS\nGAOTU\nGAOUR\nGAPED\nGAPER\nGAPES\nGAPIK\nGAPPA\nGAPPY\nGARAY\nGARBO\nGARBS\nGARCH\nGARDA\nGARDE\nGARDS\nGAREY\nGARGE\nGARON\nGAROS\nGARRI\nGARRO\nGARRS\nGARRY\nGARST\nGARTH\nGARUA\nGARUM\nGARZA\nGARZE\nGASCA\nGASER\nGASES\nGASHI\nGASPE\nGASPS\nGASPT\nGASPY\nGASSY\nGASTS\nGATAH\nGATAS\nGATCH\nGATED\nGATES\nGATHA\nGATHO\nGATKA\nGATOR\nGATRA\nGATTA\nGATTI\nGAUDS\nGAUDY\nGAUER\nGAUGE\nGAULS\nGAULT\nGAUMS\nGAUMY\nGAUNA\nGAUNT\nGAUPS\nGAURA\nGAURS\nGAUSE\nGAUSS\nGAUTS\nGAUZE\nGAUZY\nGAVAR\nGAVEL\nGAVER\nGAVID\nGAVIN\nGAVOT\nGAVYN\nGAWBY\nGAWDS\nGAWED\nGAWEL\nGAWFS\nGAWKS\nGAWKY\nGAWMS\nGAWNS\nGAWPS\nGAWTH\nGAYAL\nGAYBO\nGAYBY\nGAYED\nGAYER\nGAYLA\nGAYLE\nGAYLY\nGAYMO\nGAYNE\nGAZAL\nGAZAN\nGAZAR\nGAZDA\nGAZED\nGAZEE\nGAZEL\nGAZER\nGAZES\nGAZET\nGAZID\nGAZON\nGAZOO\nGAZZA\nGCNSS\nGCSES\nGCWRS\nGDNAS\nGEACH\nGEALS\nGEANS\nGEARE\nGEARS\nGEARY\nGEASA\nGEATS\nGEAUX\nGEBOS\nGEBRE\nGEBUR\nGECKO\nGECKS\nGEDDS\nGEDGE\nGEECH\nGEEKS\nGEEKY\nGEEPS\nGEERS\nGEESE\nGEEST\nGEHLS\nGEHRS\nGEHUS\nGEIBS\nGEIKO\nGEILS\nGEIRE\nGEISE\nGEISH\nGEIST\nGEJIA\nGELAO\nGELDS\nGELEE\nGELID\nGELLO\nGELLS\nGELLY\nGELTS\nGELUG\nGEMAS\nGEMEL\nGEMMA\nGEMMY\nGEMOT\nGENAL\nGENAM\nGENAO\nGENCO\nGENES\nGENET\nGENGS\nGENIC\nGENIE\nGENII\nGENIN\nGENIO\nGENIP\nGENNA\nGENNY\nGENOA\nGENON\nGENOS\nGENRE\nGENRO\nGENTA\nGENTS\nGENTY\nGENTZ\nGENUA\nGENUS\nGEODE\nGEOFF\nGEOID\nGEONS\nGEPID\nGERAH\nGERBE\nGERBS\nGERDA\nGEREN\nGERES\nGERIG\nGERIM\nGERIS\nGERKE\nGERMS\nGERMY\nGERNS\nGEROS\nGEROW\nGERRA\nGERRI\nGERRY\nGERSH\nGERST\nGERTH\nGERTZ\nGESHE\nGESHO\nGESKE\nGESSO\nGESTS\nGETAS\nGETER\nGETGO\nGETTS\nGETTY\nGETUP\nGEUMS\nGEWEX\nGEWOG\nGEYER\nGEZVE\nGFCIS\nGHANA\nGHANI\nGHANS\nGHAST\nGHATS\nGHAUS\nGHAUT\nGHAYN\nGHAZI\nGHEDE\nGHEEN\nGHEES\nGHENT\nGHERE\nGHESS\nGHITS\nGHIYA\nGHODS\nGHOLE\nGHOOD\nGHOSH\nGHOST\nGHOTI\nGHOUL\nGHUSL\nGHUZZ\nGHYLL\nGIANA\nGIANT\nGIBBS\nGIBBY\nGIBED\nGIBEL\nGIBER\nGIBES\nGIBLI\nGIBUN\nGIBUS\nGIDDY\nGIDJI\nGIENS\nGIESE\nGIFED\nGIFFY\nGIFTS\nGIFTY\nGIGER\nGIGOT\nGIGUE\nGIHON\nGIING\nGIIPS\nGIJON\nGILAN\nGILAS\nGILBY\nGILDS\nGILES\nGILET\nGILFS\nGILIA\nGILLE\nGILLS\nGILLY\nGILPY\nGILSE\nGILTS\nGIMEL\nGIMLI\nGIMME\nGIMPS\nGIMPY\nGINAS\nGINCH\nGINES\nGINGE\nGINGS\nGINKS\nGINNS\nGINNY\nGINOS\nGINZO\nGIPES\nGIPPO\nGIPPY\nGIPSY\nGIRAH\nGIRBY\nGIRDS\nGIRES\nGIRIS\nGIRLE\nGIRLF\nGIRLS\nGIRLY\nGIRLZ\nGIRMA\nGIRNS\nGIROD\nGIRON\nGIROS\nGIRSH\nGIRTH\nGIRTS\nGIRUS\nGIRYA\nGISED\nGISLE\nGISMO\nGISMU\nGISSA\nGISTS\nGITCH\nGITES\nGITMO\nGITTY\nGIUEN\nGIVAN\nGIVED\nGIVEE\nGIVEN\nGIVER\nGIVES\nGIZAH\nGIZMO\nGIZZA\nGIZZI\nGLACE\nGLADE\nGLADS\nGLADY\nGLAIK\nGLAIR\nGLAKY\nGLAMS\nGLAND\nGLANS\nGLARE\nGLARK\nGLARY\nGLASS\nGLATT\nGLAUM\nGLAUR\nGLAVE\nGLAZE\nGLAZY\nGLBTI\nGLBTQ\nGLEAD\nGLEAM\nGLEAN\nGLEBA\nGLEBE\nGLEBY\nGLEDE\nGLEED\nGLEEK\nGLEEM\nGLEEN\nGLEES\nGLEET\nGLEGS\nGLEIM\nGLEIS\nGLENE\nGLENN\nGLENS\nGLENT\nGLEWS\nGLEYS\nGLIAL\nGLIBS\nGLICK\nGLIDE\nGLIFF\nGLIFT\nGLIKE\nGLIMS\nGLINA\nGLINT\nGLIPS\nGLISS\nGLIST\nGLITZ\nGLOAK\nGLOAM\nGLOAR\nGLOAT\nGLOBE\nGLOBS\nGLOBY\nGLOCK\nGLODE\nGLOGG\nGLOME\nGLOMP\nGLOMS\nGLOND\nGLOOK\nGLOOM\nGLOOP\nGLOPE\nGLOPS\nGLORE\nGLORY\nGLOSA\nGLOSS\nGLOST\nGLOUR\nGLOUT\nGLOVE\nGLOWR\nGLOWS\nGLOWY\nGLOYD\nGLOZE\nGLUBS\nGLUCK\nGLUED\nGLUER\nGLUES\nGLUEY\nGLUGS\nGLUME\nGLUMP\nGLUMS\nGLUNT\nGLUON\nGLUTE\nGLUTS\nGLWTS\nGLYDE\nGLYME\nGLYNN\nGLYNS\nGLYPE\nGLYPH\nGMAIL\nGMATS\nGMDSS\nGMINA\nGMING\nGMPLS\nGMVLS\nGNAFF\nGNAGY\nGNARL\nGNARS\nGNASH\nGNASS\nGNAST\nGNATS\nGNAWA\nGNAWN\nGNAWS\nGNIDE\nGNITS\nGNODS\nGNOFF\nGNOLL\nGNOME\nGNUDI\nGNVQS\nGOADS\nGOAFS\nGOALS\nGOAMS\nGOAND\nGOANS\nGOATS\nGOATY\nGOBAN\nGOBAR\nGOBBY\nGOBEL\nGOBEN\nGOBER\nGOBHI\nGOBIN\nGOBLE\nGOBOS\nGODAS\nGODBY\nGODDA\nGODDY\nGODEK\nGODET\nGODIN\nGODIS\nGODLY\nGODOY\nGODSO\nGOEDE\nGOEKE\nGOELS\nGOELZ\nGOENS\nGOERS\nGOEST\nGOETH\nGOETY\nGOETZ\nGOFER\nGOFFS\nGOFIO\nGOGEL\nGOGES\nGOGGA\nGOGOL\nGOGOS\nGOHLS\nGOHNS\nGOIAS\nGOIIM\nGOING\nGOINS\nGOITS\nGOJES\nGOJKO\nGOJRI\nGOKEY\nGOLAN\nGOLAY\nGOLDA\nGOLDE\nGOLDS\nGOLDY\nGOLEM\nGOLEY\nGOLFS\nGOLFY\nGOLGI\nGOLLA\nGOLLI\nGOLLS\nGOLLY\nGOLOK\nGOLPE\nGOLTZ\nGOLUB\nGOLVA\nGOMAD\nGOMAN\nGOMAS\nGOMBO\nGOMEL\nGOMER\nGOMEZ\nGOMPA\nGONAD\nGONCE\nGONCH\nGONDA\nGONDI\nGONDS\nGONEF\nGONER\nGONEY\nGONGE\nGONGO\nGONGS\nGONIA\nGONIF\nGONIO\nGONJA\nGONKS\nGONNA\nGONNO\nGONOF\nGONYS\nGONZO\nGOOCH\nGOODE\nGOODO\nGOODS\nGOODY\nGOOED\nGOOEY\nGOOFS\nGOOFY\nGOOGE\nGOOGS\nGOOID\nGOOKS\nGOOKY\nGOOLD\nGOOLE\nGOOLY\nGOOMS\nGOONA\nGOONS\nGOONY\nGOOPS\nGOOPY\nGOORI\nGOOSE\nGOOSH\nGOOSY\nGOPAK\nGOPAL\nGOPAR\nGOPER\nGOPIK\nGOPIS\nGOPUZ\nGORAL\nGORAN\nGORAS\nGORBY\nGORCE\nGORDO\nGORDS\nGORDY\nGORED\nGOREE\nGOREN\nGORER\nGORES\nGOREY\nGORGE\nGORIN\nGORIS\nGORKI\nGORKS\nGORKY\nGORMS\nGORMY\nGORNO\nGORNY\nGORRA\nGORRS\nGORRU\nGORRY\nGORSE\nGORSY\nGOSAN\nGOSAS\nGOSES\nGOSHA\nGOSHT\nGOSIP\nGOSTS\nGOTAY\nGOTCH\nGOTES\nGOTHA\nGOTHS\nGOTHY\nGOTOS\nGOTRA\nGOTTA\nGOTTI\nGOTTS\nGOUDA\nGOUDS\nGOUDY\nGOUGE\nGOUGH\nGOUIN\nGOULD\nGOUND\nGOUNG\nGOURA\nGOURD\nGOURS\nGOUTS\nGOUTY\nGOVAN\nGOVEA\nGOVER\nGOVES\nGOVTS\nGOVVY\nGOWAN\nGOWAR\nGOWDA\nGOWDY\nGOWEN\nGOWER\nGOWIN\nGOWKS\nGOWLS\nGOWND\nGOWNS\nGOYAL\nGOYEM\nGOYER\nGOYIM\nGOYLE\nGOZAS\nGPCRS\nGPIOS\nGPLED\nGPPSS\nGPSED\nGPSES\nGPUSA\nGRAAL\nGRABS\nGRACE\nGRADE\nGRADO\nGRADS\nGRADY\nGRAEF\nGRAFF\nGRAFS\nGRAFT\nGRAGG\nGRAHL\nGRAHN\nGRAIL\nGRAIN\nGRAMA\nGRAME\nGRAMP\nGRAMS\nGRANA\nGRAND\nGRANE\nGRANO\nGRANS\nGRANT\nGRAPE\nGRAPH\nGRAPY\nGRASE\nGRASP\nGRASS\nGRATE\nGRATS\nGRATZ\nGRAUE\nGRAUL\nGRAVE\nGRAVS\nGRAVY\nGRAWL\nGRAYS\nGRAZE\nGREAR\nGREAT\nGREBE\nGREBO\nGREBS\nGRECE\nGREED\nGREEK\nGREEN\nGREER\nGREES\nGREET\nGREGA\nGREGG\nGREGO\nGREIG\nGREIN\nGREIP\nGREIT\nGRELL\nGRENZ\nGREPS\nGRESH\nGRETA\nGREWE\nGREYS\nGRICE\nGRIDE\nGRIDS\nGRIEB\nGRIEF\nGRIEP\nGRIER\nGRIES\nGRIFF\nGRIFT\nGRIGG\nGRIGS\nGRIKE\nGRIKO\nGRILF\nGRILL\nGRILT\nGRIME\nGRIMM\nGRIMS\nGRIMY\nGRIND\nGRINS\nGRIOT\nGRIPE\nGRIPS\nGRIPT\nGRIPY\nGRISE\nGRISM\nGRIST\nGRISY\nGRITH\nGRITS\nGRIZE\nGRIZZ\nGRNAS\nGROAN\nGROAT\nGROBE\nGROBS\nGROCE\nGROCK\nGRODY\nGROEN\nGROFF\nGROFT\nGROGG\nGROGS\nGROHL\nGROHS\nGROID\nGROIN\nGROKS\nGROMA\nGROMS\nGRONE\nGRONK\nGROOK\nGROOM\nGROOP\nGROPE\nGROPP\nGROPY\nGROSE\nGROSS\nGROSZ\nGROTE\nGROTH\nGROTS\nGROUD\nGROUP\nGROUT\nGROVE\nGROVY\nGROWE\nGROWL\nGROWN\nGROWS\nGROZE\nGRRLS\nGRRRL\nGRRRR\nGRUBB\nGRUBS\nGRUDS\nGRUED\nGRUEL\nGRUEN\nGRUES\nGRUFF\nGRUFT\nGRUHN\nGRUID\nGRUIT\nGRUME\nGRUMP\nGRUNT\nGRUPE\nGRYDE\nGRYKE\nGRYME\nGRYMS\nGRYPE\nGRYPT\nGSELL\nGSIGN\nGSKEW\nGSSPS\nGTIDS\nGTINS\nGTLDS\nGUACO\nGUAGE\nGUAIC\nGUAJE\nGUALE\nGUANA\nGUANO\nGUANS\nGUARA\nGUARD\nGUARS\nGUASA\nGUATS\nGUAVA\nGUAYS\nGUAZU\nGUBER\nGUBUS\nGUCKS\nGUDES\nGUDGE\nGUDOK\nGUELF\nGUELS\nGUESS\nGUEST\nGUEVI\nGUEYE\nGUFFS\nGUGAS\nGUGEL\nGUHRL\nGUIAC\nGUIBS\nGUICE\nGUIDA\nGUIDE\nGUIDI\nGUIDO\nGUIDS\nGUIGE\nGUIJO\nGUILD\nGUILE\nGUILL\nGUILT\nGUINN\nGUINS\nGUIRA\nGUIRL\nGUIRO\nGUISE\nGUIST\nGUITY\nGUJAR\nGUJIA\nGUJJU\nGULAE\nGULAG\nGULAL\nGULAR\nGULAS\nGULCH\nGULES\nGULET\nGULFS\nGULFY\nGULLA\nGULLO\nGULLS\nGULLY\nGULPH\nGULPS\nGULPY\nGUMBA\nGUMBE\nGUMBO\nGUMMA\nGUMMI\nGUMMY\nGUMPH\nGUMPS\nGUNAI\nGUNAS\nGUNCH\nGUNDI\nGUNDY\nGUNFU\nGUNGE\nGUNGS\nGUNGY\nGUNIA\nGUNJA\nGUNKS\nGUNKY\nGUNMA\nGUNNA\nGUNNY\nGUNTS\nGUNYA\nGUOYU\nGUPPY\nGUPTA\nGUQIN\nGURAS\nGURGE\nGURIA\nGURLS\nGURLY\nGURMY\nGURNS\nGUROO\nGURPS\nGURRS\nGURRY\nGURTS\nGURUS\nGUSAN\nGUSAU\nGUSES\nGUSHT\nGUSHY\nGUSII\nGUSLA\nGUSLE\nGUSLI\nGUSSY\nGUSTO\nGUSTS\nGUSTY\nGUTHS\nGUTKA\nGUTSY\nGUTTA\nGUTTY\nGUUAM\nGUWAR\nGUYED\nGUYER\nGUYNN\nGUYOT\nGUYSE\nGUZES\nGUZIK\nGUZLA\nGUZZI\nGUZZO\nGUZZY\nGVOZD\nGVWRS\nGWAAI\nGWALL\nGWEEP\nGWENO\nGWENT\nGWERE\nGWERZ\nGWINE\nGWINN\nGWINS\nGWUAP\nGWYNN\nGYAHO\nGYALL\nGYALS\nGYANT\nGYBED\nGYBES\nGYGER\nGYILS\nGYLES\nGYMED\nGYNAE\nGYNES\nGYNIE\nGYNOS\nGYOJI\nGYOZA\nGYPES\nGYPPO\nGYPPY\nGYPSE\nGYPSY\nGYRAE\nGYRAL\nGYRED\nGYRES\nGYRON\nGYROS\nGYRUS\nGYSES\nGYVED\nGYVES\nGZHEL\nGZIPS\nHAAKE\nHAANS\nHAARP\nHAARS\nHAART\nHAASE\nHAAST\nHABBO\nHABER\nHABIB\nHABIT\nHABLE\nHABTM\nHABUS\nHACCP\nHACEK\nHACKS\nHACKY\nHADAD\nHADAL\nHADDA\nHADDY\nHADED\nHADEN\nHADES\nHADIS\nHADJE\nHADJI\nHADNA\nHADST\nHADUD\nHADZA\nHAEKE\nHAEMO\nHAEMS\nHAENS\nHAETS\nHAFIZ\nHAFTA\nHAFTS\nHAGAN\nHAGAR\nHAGEN\nHAGER\nHAGGE\nHAGGI\nHAGGS\nHAGIN\nHAGLE\nHAGMA\nHAGOP\nHAGUE\nHAGYS\nHAHAS\nHAHMS\nHAIDA\nHAIDS\nHAIFA\nHAIGH\nHAIKS\nHAIKU\nHAILE\nHAILL\nHAILS\nHAILU\nHAILY\nHAINS\nHAINT\nHAIRE\nHAIRS\nHAIRY\nHAITH\nHAITI\nHAIXI\nHAJEK\nHAJES\nHAJIB\nHAJIS\nHAJJI\nHAKAM\nHAKAS\nHAKEA\nHAKED\nHAKEN\nHAKES\nHAKHA\nHAKIM\nHAKKA\nHAKOB\nHAKOS\nHALAB\nHALAL\nHALAU\nHALDI\nHALEB\nHALED\nHALER\nHALES\nHALEY\nHALFA\nHALFE\nHALFS\nHALGI\nHALID\nHALIM\nHALKI\nHALKO\nHALLE\nHALLO\nHALLS\nHALLY\nHALMA\nHALMS\nHALON\nHALOO\nHALOS\nHALPS\nHALSE\nHALTS\nHALVA\nHALVE\nHALWA\nHAMAD\nHAMAL\nHAMAM\nHAMAN\nHAMAS\nHAMBO\nHAMBY\nHAMED\nHAMEL\nHAMER\nHAMES\nHAMID\nHAMIL\nHAMMY\nHAMON\nHAMPS\nHAMRE\nHAMSA\nHAMZA\nHANAE\nHANAI\nHANAN\nHANAP\nHANBY\nHANCE\nHANCH\nHANDE\nHANDI\nHANDS\nHANDY\nHANEL\nHANER\nHANES\nHANEY\nHANGE\nHANGI\nHANGS\nHANIA\nHANIF\nHANJA\nHANKE\nHANKO\nHANKS\nHANKY\nHANLY\nHANNA\nHANNS\nHANOI\nHANOK\nHANOR\nHANSE\nHANTS\nHANYU\nHANZI\nHAOLE\nHAOMA\nHAORA\nHAORS\nHAPAS\nHAPAX\nHAPLY\nHAPPI\nHAPPY\nHAPUS\nHAQUE\nHARAM\nHARAN\nHARAS\nHARBS\nHARCH\nHARDS\nHARDT\nHARDY\nHARED\nHAREM\nHAREN\nHARER\nHARES\nHARIG\nHARIM\nHARJO\nHARKI\nHARKS\nHARLE\nHARLS\nHARMS\nHARNS\nHARNT\nHARPE\nHARPS\nHARPY\nHARRS\nHARRY\nHARSE\nHARSH\nHARTE\nHARTH\nHARTI\nHARTL\nHARTS\nHARTT\nHARTY\nHARTZ\nHARUN\nHARUO\nHASES\nHASHI\nHASHY\nHASID\nHASKS\nHASMA\nHASNA\nHASPS\nHASSA\nHASSE\nHASTA\nHASTE\nHASTY\nHATCH\nHATED\nHATEE\nHATEL\nHATEM\nHATER\nHATES\nHATHA\nHATHE\nHATIF\nHATOS\nHATRA\nHATTA\nHATTI\nHATTY\nHAUCH\nHAUCK\nHAUFF\nHAUGH\nHAUGS\nHAUKE\nHAULM\nHAULS\nHAULT\nHAUMS\nHAUNS\nHAUNT\nHAUPT\nHAUSA\nHAUSE\nHAVAN\nHAVEL\nHAVEN\nHAVER\nHAVES\nHAVEY\nHAVOC\nHAVRE\nHAVTA\nHAWAY\nHAWED\nHAWES\nHAWKE\nHAWKS\nHAWKY\nHAWMS\nHAWNS\nHAWSE\nHAWTS\nHAXOR\nHAYAH\nHAYAS\nHAYDN\nHAYED\nHAYEK\nHAYER\nHAYES\nHAYEY\nHAYLE\nHAYNE\nHAYSE\nHAYSI\nHAYTI\nHAZAN\nHAZED\nHAZEE\nHAZEL\nHAZEN\nHAZER\nHAZES\nHAZLE\nHBARS\nHBCUS\nHCDNA\nHCFCS\nHCOOH\nHDQRS\nHEADS\nHEADY\nHEAGY\nHEALD\nHEALS\nHEALY\nHEAMS\nHEANS\nHEAPS\nHEAPY\nHEARD\nHEARE\nHEARK\nHEARN\nHEARO\nHEARS\nHEART\nHEAST\nHEATE\nHEATH\nHEATS\nHEATY\nHEAUY\nHEAVE\nHEAVY\nHEBEI\nHEBEN\nHEBER\nHEBES\nHECKA\nHECKS\nHEDER\nHEDGE\nHEDGY\nHEDIN\nHEDON\nHEEBS\nHEEDS\nHEEDY\nHEEEY\nHEELD\nHEELS\nHEEPS\nHEERD\nHEERE\nHEERS\nHEEZE\nHEFCE\nHEFEI\nHEFTS\nHEFTY\nHEGDE\nHEGEL\nHEGER\nHEGGE\nHEGGS\nHEIAU\nHEIDI\nHEIDS\nHEIDT\nHEIER\nHEIGH\nHEIHE\nHEIJO\nHEILS\nHEINE\nHEINS\nHEINZ\nHEIRE\nHEIRS\nHEISE\nHEIST\nHEITA\nHEITI\nHEITS\nHEITZ\nHEJAB\nHEJAZ\nHEJRA\nHELDT\nHELED\nHELEN\nHELES\nHELEY\nHELGA\nHELIO\nHELIX\nHELLA\nHELLO\nHELLP\nHELLS\nHELLY\nHELMS\nHELOC\nHELOS\nHELOT\nHELPE\nHELPS\nHELPT\nHELTH\nHELTS\nHELVE\nHEMAL\nHEMAN\nHEMBY\nHEMEL\nHEMES\nHEMIC\nHEMIN\nHEMME\nHEMPS\nHEMPY\nHENAN\nHENCE\nHENCH\nHENDE\nHENDS\nHENDY\nHENGE\nHENGS\nHENID\nHENKE\nHENKS\nHENNA\nHENNE\nHENNY\nHENRY\nHENTS\nHENTZ\nHENZE\nHEPAR\nHEPES\nHEPPS\nHERAT\nHERBS\nHERBY\nHERDS\nHEREM\nHERES\nHERLS\nHERMA\nHERMS\nHERNE\nHERNS\nHEROA\nHEROD\nHEROE\nHERON\nHEROS\nHERPS\nHERRO\nHERRY\nHERSE\nHERSH\nHERTS\nHERTZ\nHESCS\nHESHE\nHESPS\nHESSE\nHESTS\nHETAS\nHETHS\nHETOL\nHETRO\nHETTY\nHEUGH\nHEUKS\nHEUNS\nHEVEA\nHEVVA\nHEWAT\nHEWED\nHEWER\nHEWES\nHEWWO\nHEXAD\nHEXED\nHEXER\nHEXES\nHEXIC\nHEXIT\nHEXOL\nHEXON\nHEXYL\nHEYDT\nHEYEM\nHEYEN\nHEYER\nHEYLS\nHEYNE\nHEYNS\nHEZBO\nHGPRT\nHGWYS\nHIATT\nHIBBS\nHIBLE\nHICES\nHICKS\nHIDED\nHIDEL\nHIDEO\nHIDER\nHIDES\nHIDLE\nHIELD\nHIETT\nHIFFS\nHIGAB\nHIGAS\nHIGBY\nHIGGS\nHIGHS\nHIGHT\nHIGRE\nHIJAB\nHIJAZ\nHIJRA\nHIJRI\nHIKED\nHIKER\nHIKES\nHIKOI\nHILAL\nHILAR\nHILCH\nHILDA\nHILDS\nHILER\nHILES\nHILLO\nHILLS\nHILLY\nHILMA\nHILOT\nHILSA\nHILTS\nHILTY\nHILTZ\nHILUM\nHILUS\nHIMBO\nHIMEL\nHIMYM\nHINCK\nHINDE\nHINDI\nHINDS\nHINDU\nHINER\nHINES\nHINEY\nHINGE\nHINGS\nHINKS\nHINKY\nHINNY\nHINTS\nHINTZ\nHINZE\nHIOTT\nHIPAA\nHIPED\nHIPER\nHIPES\nHIPLY\nHIPPO\nHIPPS\nHIPPY\nHIRAI\nHIRAM\nHIRDS\nHIRED\nHIREE\nHIREN\nHIRER\nHIRES\nHIRIK\nHIRIQ\nHIRNS\nHIRSH\nHIRST\nHIRTH\nHISEL\nHISER\nHISES\nHISEY\nHISLE\nHISPI\nHISSY\nHISTO\nHISTS\nHITCH\nHITES\nHITHE\nHITTS\nHITUP\nHIVED\nHIVER\nHIVES\nHIWIS\nHIXES\nHIXON\nHIZZY\nHJELM\nHKSAR\nHLEGU\nHLIRG\nHLLVS\nHMMED\nHMMPH\nHMMWV\nHMNZS\nHMONG\nHMPHS\nHMTSS\nHMXBS\nHNELS\nHNLMS\nHNNNG\nHNOMS\nHNRNA\nHNRNP\nHOAGS\nHOAGY\nHOAKS\nHOAKY\nHOANG\nHOARD\nHOARE\nHOARS\nHOARY\nHOAST\nHOBAG\nHOBAN\nHOBBS\nHOBBY\nHOBDY\nHOBEI\nHOBIT\nHOBLS\nHOBOE\nHOBOS\nHOBOY\nHOCCO\nHOCKS\nHOCUM\nHOCUS\nHODAD\nHODAG\nHODEL\nHODES\nHODGE\nHODJA\nHODLS\nHODOS\nHOEFT\nHOEHN\nHOELS\nHOERR\nHOERS\nHOEYS\nHOFEI\nHOFER\nHOFFA\nHOFUL\nHOGAN\nHOGEL\nHOGES\nHOGGE\nHOGGS\nHOGGY\nHOGHS\nHOGLE\nHOGUE\nHOHLS\nHOICK\nHOIDA\nHOIHO\nHOIKS\nHOISE\nHOIST\nHOITS\nHOKAN\nHOKED\nHOKER\nHOKES\nHOKEY\nHOKIE\nHOKKU\nHOKLO\nHOKUM\nHOKYO\nHOLAP\nHOLDE\nHOLDS\nHOLED\nHOLEN\nHOLER\nHOLES\nHOLEY\nHOLIE\nHOLIN\nHOLKS\nHOLLA\nHOLLE\nHOLLI\nHOLLO\nHOLLS\nHOLLY\nHOLME\nHOLMS\nHOLON\nHOLOS\nHOLTE\nHOLTS\nHOLTZ\nHOMAM\nHOMAN\nHOMAS\nHOMED\nHOMEE\nHOMEL\nHOMER\nHOMES\nHOMEY\nHOMIE\nHOMOS\nHOMSI\nHONAN\nHONDA\nHONDO\nHONEA\nHONED\nHONER\nHONES\nHONEY\nHONGI\nHONGS\nHONIE\nHONKS\nHONKY\nHONNE\nHONNS\nHONOR\nHONTZ\nHONUS\nHOOAH\nHOOCH\nHOODS\nHOODY\nHOOEY\nHOOFS\nHOOFY\nHOOIE\nHOOKA\nHOOKE\nHOOKS\nHOOKY\nHOOLE\nHOOLS\nHOOLY\nHOOND\nHOONS\nHOOPS\nHOOPY\nHOORD\nHOOSE\nHOOSH\nHOOTS\nHOOTY\nHOOVE\nHOPAK\nHOPED\nHOPEH\nHOPEI\nHOPER\nHOPES\nHOPFS\nHOPIA\nHOPIS\nHOPLO\nHOPPE\nHOPPO\nHOPPS\nHOPPY\nHOQUE\nHORAE\nHORAH\nHORAI\nHORAK\nHORAL\nHORAN\nHORAS\nHORDE\nHORDS\nHORIS\nHORKS\nHORNE\nHORNS\nHORNY\nHORRY\nHORSE\nHORST\nHORSY\nHORTA\nHORUS\nHOSEA\nHOSED\nHOSEL\nHOSEN\nHOSER\nHOSES\nHOSEY\nHOSHO\nHOSPO\nHOSTA\nHOSTS\nHOTAN\nHOTCH\nHOTEI\nHOTEL\nHOTEP\nHOTHS\nHOTLY\nHOTOT\nHOTRS\nHOTTY\nHOUCK\nHOUDE\nHOUGH\nHOULD\nHOULE\nHOULT\nHOUMA\nHOUND\nHOUPT\nHOURE\nHOURI\nHOURS\nHOUSE\nHOUSH\nHOUSS\nHOUTS\nHOUTZ\nHOUZE\nHOVAN\nHOVAS\nHOVDE\nHOVED\nHOVEL\nHOVEN\nHOVER\nHOVES\nHOVEY\nHOWAT\nHOWAY\nHOWBE\nHOWDY\nHOWEL\nHOWER\nHOWES\nHOWEY\nHOWFF\nHOWFS\nHOWGH\nHOWIE\nHOWJA\nHOWLE\nHOWLS\nHOWPS\nHOWSE\nHOWSO\nHOWTO\nHOWVE\nHOWZE\nHOXED\nHOXES\nHOXHA\nHOXIE\nHOXIT\nHOYAS\nHOYAY\nHOYED\nHOYER\nHOYES\nHOYIN\nHOYLE\nHOYTE\nHPIVS\nHPSCS\nHRACH\nHRBPS\nHREBS\nHRENS\nHRITZ\nHRMPH\nHRUBY\nHSCSD\nHSCTS\nHSDNM\nHSDPA\nHSECN\nHSERS\nHSIAO\nHSIAS\nHSIEH\nHSIEN\nHSUEH\nHSUPA\nHSWMS\nHSYNC\nHTOOS\nHTPCS\nHTTPD\nHTTPS\nHUANG\nHUARD\nHUAVE\nHUAXI\nHUBAL\nHUBAY\nHUBBY\nHUBEI\nHUBER\nHUBUS\nHUCED\nHUCKS\nHUCOW\nHUDAK\nHUDDY\nHUDEC\nHUDGE\nHUDNA\nHUDON\nHUDUD\nHUERS\nHUETT\nHUEVO\nHUEYS\nHUEZO\nHUFFS\nHUFFY\nHUGAG\nHUGEN\nHUGER\nHUGGS\nHUGGY\nHUGOS\nHUHUS\nHUIAS\nHUIES\nHUITT\nHUJRA\nHUKES\nHUKIN\nHUKKA\nHUKOU\nHULAN\nHULAS\nHULCH\nHULEN\nHULET\nHULIN\nHULKS\nHULKY\nHULLO\nHULLS\nHULLY\nHULME\nHULSE\nHULTS\nHUMAN\nHUMET\nHUMFS\nHUMIC\nHUMID\nHUMIN\nHUMMS\nHUMMY\nHUMOR\nHUMPH\nHUMPS\nHUMPY\nHUMUS\nHUNAN\nHUNCH\nHUNDI\nHUNDO\nHUNDT\nHUNDY\nHUNGS\nHUNKS\nHUNKY\nHUNNY\nHUNTS\nHUNTY\nHUOTS\nHUPAH\nHUPAS\nHUPEH\nHUPIA\nHUPOT\nHUPPS\nHUQAS\nHUQIN\nHUQQA\nHURAL\nHURDS\nHURLS\nHURLY\nHURNS\nHURON\nHURRA\nHURRS\nHURRY\nHURSH\nHURST\nHURTE\nHURTS\nHURTT\nHURTY\nHUSAK\nHUSBY\nHUSER\nHUSES\nHUSHT\nHUSHY\nHUSKS\nHUSKY\nHUSON\nHUSOS\nHUSSY\nHUSTS\nHUTCH\nHUTHS\nHUTIA\nHUTTO\nHUTUS\nHUVAL\nHUXES\nHUYCK\nHUYNH\nHUZUN\nHUZZA\nHVACS\nHWAIR\nHWANG\nHWANS\nHYANG\nHYATT\nHYAWA\nHYCHE\nHYDEL\nHYDEN\nHYDER\nHYDES\nHYDRA\nHYDRO\nHYENA\nHYENS\nHYEST\nHYGGE\nHYGHT\nHYING\nHYIPS\nHYKES\nHYLEG\nHYLER\nHYLIC\nHYLID\nHYMAN\nHYMEL\nHYMEN\nHYMER\nHYMIE\nHYMNS\nHYNDE\nHYOGO\nHYOID\nHYOTE\nHYPED\nHYPER\nHYPES\nHYPHA\nHYPHY\nHYPOS\nHYPSD\nHYPSM\nHYRAX\nHYRES\nHYRSE\nHYRUM\nHYSON\nHYTHE\nHYUNS\nHYZER\nHZRGS\nIAAPA\nIAIDO\nIAMAT\nIAMBI\nIAMBS\nIAMFI\nIAMID\nIAMMS\nIANAD\nIANAE\nIANAL\nIANAS\nIANNI\nIARTK\nIASPS\nIAWTC\nIAWTP\nIBADA\nIBADI\nIBANS\nIBATS\nIBELL\nIBIZA\nIBLIS\nIBMER\nIBOGA\nIBOOK\nIBRIK\nIBSON\nICANN\nICASM\nICBMS\nICCDS\nICCPR\nICDLS\nICENI\nICENS\nICERS\nICEVS\nICFVS\nICHOR\nICIER\nICILY\nICING\nICKER\nICKLE\nICMES\nICNCP\nICONS\nICQED\nICQER\nICTAL\nICTIC\nICTUS\nICWUC\nICYDK\nICYMI\nICYWW\nIDAAS\nIDAES\nIDAFA\nIDAHO\nIDANT\nIDDAH\nIDDAT\nIDEAL\nIDEAN\nIDEAR\nIDEAS\nIDEAT\nIDEES\nIDELE\nIDENT\nIDEOT\nIDERS\nIDGAF\nIDGAS\nIDGET\nIDING\nIDIOM\nIDIOT\nIDISM\nIDIST\nIDJIT\nIDJUT\nIDLED\nIDLER\nIDLES\nIDLIS\nIDMCS\nIDOLA\nIDOLL\nIDOLS\nIDOSE\nIDPOL\nIDREN\nIDRIS\nIDYLL\nIDYLS\nIELTS\nIESHA\nIESNA\nIESUS\nIFDEF\nIFERE\nIFFEN\nIFIDS\nIFILL\nIFMAS\nIFRIT\nIFSMA\nIFTAR\nIFTTT\nIGAPO\nIGBOS\nIGBTS\nIGCSE\nIGGED\nIGGIE\nIGILS\nIGLOO\nIGLUS\nIGOES\nIGOUS\nIHDES\nIHLES\nIHOOD\nIHRAM\nIHRIG\nIHRKE\nIIFES\nIIOPS\nIIPAS\nIJAWS\nIJAZA\nIJOID\nIKATS\nIKEDA\nIKERD\nIKEYS\nIKIZU\nIKKAT\nIKNER\nIKONS\nIKTAR\nIKUKO\nIKUMI\nILALA\nILAMA\nILAND\nILEAC\nILEAL\nILECS\nILERS\nILEUM\nILEUS\nILHAM\nILIAC\nILIAD\nILIAL\nILIAN\nILICS\nILIFF\nILINX\nILION\nILISH\nILITY\nILIUM\nILLER\nILLIG\nILLOS\nILLTH\nILLUI\nILONA\nILOOY\nILYSM\nIMAAM\nIMACS\nIMAGE\nIMAGO\nIMAMS\nIMANI\nIMANS\nIMARI\nIMAUM\nIMBAR\nIMBAT\nIMBAY\nIMBED\nIMBER\nIMBES\nIMBOW\nIMBOX\nIMBUE\nIMEIS\nIMELL\nIMELS\nIMENE\nIMERS\nIMHOF\nIMIDE\nIMIDO\nIMIDS\nIMINE\nIMINO\nIMINS\nIMLAY\nIMLER\nIMMEL\nIMMEW\nIMMID\nIMMIE\nIMMIT\nIMMIX\nIMPED\nIMPEL\nIMPEN\nIMPEX\nIMPHM\nIMPIS\nIMPLY\nIMPOV\nIMPRO\nIMRAN\nIMSIS\nIMXBS\nINABA\nINAJA\nINANE\nINAPT\nINARI\nINATE\nINAUS\nINAWE\nINAWS\nINBOX\nINBYE\nINCAN\nINCAS\nINCEL\nINCHI\nINCLE\nINCOG\nINCUR\nINCUS\nINCUT\nINDAS\nINDEF\nINDEL\nINDEW\nINDEX\nINDIA\nINDIC\nINDIE\nINDIN\nINDOC\nINDOL\nINDON\nINDOW\nINDRA\nINDRE\nINDRI\nINDUE\nINDUS\nINEPT\nINERM\nINERT\nINEYE\nINFER\nINFIT\nINFIX\nINFON\nINFRA\nINGAS\nINGEN\nINGLE\nINGOT\nINIAD\nINIAL\nINIAS\nINIID\nINION\nINITS\nINITY\nINJEN\nINJUN\nINKAN\nINKAS\nINKED\nINKER\nINKES\nINKLE\nINKOM\nINLAW\nINLAY\nINLET\nINLOW\nINMAN\nINMEW\nINMID\nINMIX\nINMON\nINNED\nINNER\nINNES\nINNEW\nINNIE\nINNIT\nINOAS\nINODE\nINORB\nINOUE\nINPAT\nINPUT\nINRIA\nINROS\nINRUN\nINSEE\nINSET\nINSPO\nINSTA\nINSUE\nINTEL\nINTER\nINTHA\nINTIS\nINTIV\nINTRO\nINUIT\nINULA\nINURE\nINURN\nINUST\nINVAR\nINVEX\nINVIS\nINWIT\nIODAL\nIODIC\nIODID\nIODIN\nIODOL\nIOLTA\nIONIA\nIONIC\nIOQUA\nIORAS\nIOTAS\nIOWAN\nIOWAS\nIPINA\nIPOCK\nIPODS\nIPPON\nIPRGC\nIPSCS\nIPSID\nIQAMA\nIQBAL\nIQYAX\nIRAAN\nIRADE\nIRAKW\nIRANI\nIRAQI\nIRAQW\nIRATE\nIRBIL\nIRBMS\nIRBYS\nIRCED\nIRCER\nIRCOP\nIRENE\nIREYS\nIRFAN\nIRGUN\nIRIAN\nIRIBE\nIRICK\nIRIDS\nIRING\nIRINT\nIRION\nIRISH\nIRKED\nIRKUT\nIRLAM\nIRMOI\nIRMOS\nIROIJ\nIROKO\nIRONE\nIRONS\nIRONY\nIRORI\nIRQLS\nIRREP\nIRULA\nIRVEN\nIRVIN\nIRWIN\nISAAC\nISAPI\nISAZA\nISBAS\nISBNS\nISCII\nISCSI\nISDNS\nISERE\nISERS\nISEUM\nISHAK\nISHAM\nISHAN\nISHAQ\nISHAS\nISHEE\nISHES\nISHII\nISIAC\nISIAH\nISICS\nISINT\nISLAM\nISLAY\nISLER\nISLES\nISLET\nISLEY\nISNAD\nISNAE\nISNER\nISOKO\nISOLA\nISOMS\nISONS\nISPIR\nISRHA\nISRUS\nISSAC\nISSAS\nISSEI\nISSES\nISSID\nISSNS\nISSUE\nISSUS\nISTAR\nISTEA\nISTLE\nISTQB\nISTRE\nISUZU\nISWAP\nISWAS\nISWYM\nITALA\nITALO\nITALS\nITALY\nITARD\nITCHY\nITEMS\nITERS\nITHES\nITIES\nITIVE\nITTAR\nITUNA\nITZEL\nIUDAS\nIUDGE\nIUELL\nIUNNO\nIUPAC\nIUSSI\nIVAIN\nIVALO\nIVERY\nIVIED\nIVIES\nIVINS\nIVORY\nIVRIT\nIVYED\nIWANA\nIWANS\nIWARS\nIWASA\nIWATA\nIWATE\nIXIAS\nIXION\nIXNAY\nIXORA\nIXTAB\nIXTLE\nIYERS\nIYOBA\nIYORA\nIZAAR\nIZARD\nIZBAS\nIZEDI\nIZLES\nIZMEL\nIZMIR\nIZMIT\nIZORA\nIZORI\nIZUMI\nIZZAT\nIZZIE\nIZZIS\nIZZIT\nIZZOS\nJAADI\nJAALI\nJABBA\nJABBY\nJABER\nJABEZ\nJABOT\nJACAL\nJACKO\nJACKS\nJACKY\nJACOB\nJACOS\nJADED\nJADEN\nJADES\nJADON\nJADOO\nJAFAR\nJAFAS\nJAFFA\nJAFFE\nJAFFY\nJAFOS\nJAFRI\nJAGER\nJAGGS\nJAGGY\nJAGIR\nJAGRA\nJAHAI\nJAHAN\nJAHVE\nJAIDA\nJAILS\nJAIME\nJAINS\nJAKED\nJAKES\nJAKEY\nJAKIE\nJAKOS\nJAKUN\nJALAP\nJALEN\nJALEO\nJALIL\nJALIS\nJALOP\nJAMAL\nJAMAR\nJAMAS\nJAMAT\nJAMBI\nJAMBO\nJAMBS\nJAMBU\nJAMES\nJAMIE\nJAMIL\nJAMIR\nJAMMU\nJAMMY\nJAMON\nJAMOS\nJAMUN\nJANAE\nJANAK\nJANDA\nJANEA\nJANES\nJANET\nJANEU\nJANEY\nJANGS\nJANIE\nJANIS\nJANKE\nJANKO\nJANKY\nJANNA\nJANNS\nJANNY\nJANTO\nJANTS\nJANTU\nJANTY\nJANTZ\nJANUS\nJAPAN\nJAPED\nJAPER\nJAPES\nJAPHS\nJAPIE\nJAPOW\nJAPPY\nJARAI\nJARDS\nJARED\nJARKS\nJARLS\nJARON\nJARPS\nJARRA\nJARUL\nJARVI\nJARVY\nJASES\nJASEY\nJASIN\nJASMS\nJASON\nJASOS\nJASPE\nJASSO\nJATHA\nJATIS\nJATOS\nJAUMS\nJAUNT\nJAUPS\nJAVAN\nJAVAS\nJAVED\nJAVEL\nJAVON\nJAWAD\nJAWAN\nJAWAR\nJAWED\nJAWNS\nJAXEN\nJAXON\nJAYCE\nJAYDA\nJAYDE\nJAYET\nJAYLA\nJAYNE\nJAYSE\nJAZEL\nJAZZY\nJDBCS\nJEANS\nJEARS\nJEAST\nJEATS\nJEBEL\nJEBYA\nJECTS\nJEDEK\nJEDGE\nJEDIS\nJEELS\nJEEMS\nJEEPS\nJEERA\nJEERS\nJEESH\nJEEZE\nJEFES\nJEFFI\nJEFFO\nJEFFS\nJEFFY\nJEGOG\nJEHAD\nJEHAI\nJEHOL\nJEHUS\nJEJUS\nJELAB\nJELLO\nJELLS\nJELLY\nJELQS\nJEMBE\nJEMMA\nJEMMY\nJENBE\nJENGA\nJENGS\nJENKS\nJENNA\nJENNE\nJENNI\nJENNS\nJENNY\nJENTS\nJEONG\nJEONS\nJEREZ\nJERIB\nJERID\nJERKS\nJERKY\nJERRI\nJERRY\nJESKE\nJESSA\nJESSE\nJESSI\nJESTS\nJESTY\nJESUS\nJETER\nJETES\nJETON\nJETTY\nJEUDY\nJEUNE\nJEWED\nJEWEL\nJEWES\nJEWIE\nJEWRY\nJEZVE\nJEZZA\nJFETS\nJFIFS\nJFKED\nJHALA\nJHEEL\nJHILS\nJHOOM\nJHOVE\nJHUNA\nJIANG\nJIANS\nJIAOS\nJIAYI\nJIAYU\nJIBBS\nJIBED\nJIBER\nJIBES\nJIDDA\nJIFFS\nJIFFY\nJIGGY\nJIGOS\nJIGOT\nJIHAD\nJIHUN\nJIJUS\nJIKOS\nJILDI\nJILDY\nJILEK\nJILIN\nJILLS\nJILLY\nJILTS\nJIMBE\nJIMBO\nJIMMI\nJIMMY\nJIMPY\nJINAN\nJINDY\nJINGO\nJINGS\nJINJA\nJINJU\nJINKS\nJINKY\nJINNI\nJINNS\nJINNY\nJINTS\nJIRDS\nJIRGA\nJIRON\nJISEI\nJISMS\nJISTS\nJITED\nJITTY\nJIVED\nJIVER\nJIVES\nJIVEY\nJIZYA\nJIZZY\nJNANA\nJNDIS\nJNOVS\nJNTSC\nJOANN\nJOANY\nJOBBE\nJOBBY\nJOBED\nJOBES\nJOBIN\nJOCKO\nJOCKS\nJOCKY\nJODEL\nJODHS\nJODIE\nJOEYS\nJOFFE\nJOHAD\nJOHAL\nJOHAN\nJOHAR\nJOHNE\nJOHNS\nJOHNY\nJOHOR\nJOICE\nJOIKS\nJOINS\nJOINT\nJOIST\nJOKED\nJOKER\nJOKES\nJOKEY\nJOKIS\nJOLED\nJOLES\nJOLIE\nJOLIN\nJOLLS\nJOLLY\nJOLOF\nJOLTS\nJOLTY\nJOLYS\nJOMOS\nJONAH\nJONAS\nJONES\nJONGS\nJONNA\nJONTY\nJOOKS\nJOOLS\nJOPPA\nJORAM\nJORBS\nJORDY\nJORJA\nJORTS\nJORUM\nJOSEI\nJOSEY\nJOSHI\nJOSIE\nJOSUE\nJOTAS\nJOTTO\nJOTTY\nJOTUN\nJOUAL\nJOUGS\nJOUKS\nJOULE\nJOULS\nJOURS\nJOUST\nJOVEL\nJOWAN\nJOWAR\nJOWLS\nJOWLY\nJOYAL\nJOYAS\nJOYCE\nJOYED\nJOYEN\nJOYES\nJOYNE\nJOYNT\nJPDAS\nJPEGS\nJROTC\nJRPGS\nJSDKS\nJSWDK\nJTLYK\nJUANE\nJUBAE\nJUBAS\nJUBBA\nJUBBE\nJUBES\nJUCHE\nJUCOS\nJUDAH\nJUDAS\nJUDEA\nJUDGE\nJUDGY\nJUDIE\nJUELZ\nJUGAL\nJUGER\nJUGGS\nJUGUM\nJUHAR\nJUHYO\nJUICE\nJUICY\nJUJUS\nJUJUY\nJUKED\nJUKES\nJULAP\nJULEP\nJULES\nJULEY\nJULIA\nJULID\nJULIE\nJULIO\nJULIP\nJULUS\nJULYE\nJULYS\nJUMAR\nJUMAS\nJUMBI\nJUMBO\nJUMBY\nJUMNA\nJUMPS\nJUMPY\nJUMUA\nJUNCO\nJUNDT\nJUNES\nJUNIE\nJUNJO\nJUNKO\nJUNKS\nJUNKY\nJUNTA\nJUNTO\nJUPED\nJUPES\nJUPON\nJURAL\nJURAT\nJUREL\nJURIE\nJUROR\nJURYO\nJUSSI\nJUSSY\nJUSTE\nJUSTS\nJUTES\nJUTTI\nJUTTY\nJUUKA\nJUVES\nJUVEY\nJUVIA\nJUVIE\nJUXTA\nJUYCE\nJVMDI\nJVMTI\nJVPNE\nJWSDP\nKAABA\nKAAFS\nKAAMA\nKAAPS\nKAATZ\nKABAB\nKABAM\nKABAT\nKABIR\nKABOB\nKABSA\nKABUL\nKACEE\nKACHA\nKACIE\nKADAM\nKADEL\nKADEN\nKADES\nKADET\nKADHI\nKADIS\nKAEDE\nKAENS\nKAFAL\nKAFIR\nKAFIS\nKAFKA\nKAFTA\nKAGAN\nKAGES\nKAGOK\nKAGUS\nKAGYU\nKAHAL\nKAHAN\nKAHAU\nKAHLE\nKAHLS\nKAIAK\nKAIDS\nKAIJU\nKAIKI\nKAIKS\nKAILA\nKAILS\nKAIMA\nKAINI\nKAINS\nKAISO\nKAIST\nKAIYA\nKAJAL\nKAJIK\nKAJIN\nKAJIS\nKAJUS\nKAKAR\nKAKAS\nKAKDI\nKAKIE\nKAKIS\nKAKKE\nKAKRO\nKALAM\nKALAN\nKALAO\nKALBI\nKALEB\nKALEL\nKALER\nKALES\nKALEY\nKALIE\nKALIF\nKALIL\nKALIN\nKALIS\nKALON\nKALPA\nKALRA\nKALUA\nKAMAL\nKAMAS\nKAMAU\nKAMBA\nKAMBO\nKAMEN\nKAMES\nKAMIK\nKAMIS\nKAMPA\nKAMSA\nKAMUT\nKAMUY\nKANAB\nKANAK\nKANAN\nKANAS\nKANAT\nKANDA\nKANDY\nKANEH\nKANGA\nKANGO\nKANGS\nKANIA\nKANJI\nKANNA\nKANNS\nKANNY\nKANOE\nKANON\nKANOS\nKANSA\nKANSU\nKANTZ\nKANUN\nKANZO\nKANZU\nKAONS\nKAORI\nKAORU\nKAOYU\nKAPAN\nKAPAS\nKAPES\nKAPHS\nKAPIA\nKAPOK\nKAPOS\nKAPOW\nKAPPA\nKAPUR\nKAPUS\nKAPUT\nKARAJ\nKARAM\nKARAS\nKARAT\nKARBI\nKARCH\nKAREN\nKAREZ\nKARGS\nKARIM\nKARIN\nKARKI\nKARLA\nKARLE\nKARLI\nKARLY\nKARMA\nKARNA\nKARNS\nKAROB\nKAROK\nKAROO\nKAROW\nKARPS\nKARRI\nKARRS\nKARST\nKARTS\nKARTU\nKARUK\nKARWA\nKARYN\nKARYS\nKARZY\nKASEN\nKASER\nKASEY\nKASHA\nKASHI\nKASON\nKASOS\nKASRA\nKASSU\nKASTS\nKATAH\nKATAL\nKATAR\nKATAS\nKATES\nKATHA\nKATHI\nKATHY\nKATIA\nKATIE\nKATIS\nKATSU\nKATTI\nKATTY\nKATYA\nKAUAI\nKAUPS\nKAURI\nKAUTZ\nKAVAL\nKAVAS\nKAVYA\nKAWED\nKAWIT\nKAWNS\nKAYAH\nKAYAK\nKAYAS\nKAYKO\nKAYLA\nKAYLE\nKAYOS\nKAZAK\nKAZAN\nKAZEE\nKAZIS\nKAZMI\nKAZOO\nKAZUE\nKAZUO\nKBARS\nKCALS\nKEALY\nKEANE\nKEANO\nKEANS\nKEANU\nKEARA\nKEARS\nKEAST\nKEATS\nKEAVY\nKEBAB\nKEBAP\nKEBOB\nKECAK\nKECHI\nKECKS\nKEDAH\nKEDDY\nKEDGE\nKEECH\nKEEDS\nKEEFE\nKEEFS\nKEEHN\nKEEHO\nKEEKS\nKEELE\nKEELS\nKEELY\nKEEMA\nKEENA\nKEENE\nKEENO\nKEENS\nKEEPE\nKEEPS\nKEESE\nKEESH\nKEETH\nKEETS\nKEEVE\nKEFIR\nKEGEL\nKEGGY\nKEHLS\nKEHMS\nKEHNS\nKEHOE\nKEHRS\nKEHUA\nKEIJI\nKEIJO\nKEIKO\nKEIRA\nKEIRS\nKEITH\nKEITT\nKELCH\nKELEN\nKELEP\nKELIM\nKELKS\nKELLI\nKELLS\nKELLY\nKELMS\nKELPS\nKELPY\nKELSO\nKELTS\nKELTY\nKEMBS\nKEMET\nKEMPE\nKEMPF\nKEMPO\nKEMPS\nKEMPT\nKENAF\nKENAI\nKENAN\nKENCH\nKENDI\nKENDO\nKENES\nKENJI\nKENKE\nKENKY\nKENNA\nKENNY\nKENPO\nKENTA\nKENTE\nKENTS\nKENYA\nKENZO\nKEOGH\nKEOWN\nKEPIS\nKEPOS\nKERBS\nKERBY\nKERCH\nKERES\nKERFS\nKERIN\nKERIS\nKERLS\nKERMA\nKERNE\nKERNS\nKEROS\nKERRI\nKERRY\nKERSH\nKERUV\nKERVE\nKESAR\nKESAS\nKETAL\nKETAS\nKETCH\nKETOL\nKETOS\nKETYL\nKEVAN\nKEVEL\nKEVIL\nKEVIN\nKEVVY\nKEVYN\nKEWDA\nKEWRA\nKEXES\nKEYED\nKEYER\nKEYES\nKEYLA\nKEYTE\nKEZIA\nKEZZA\nKFCER\nKGALS\nKGOSI\nKHADI\nKHAEN\nKHAFD\nKHAFS\nKHAKI\nKHANA\nKHANG\nKHANS\nKHAPH\nKHASA\nKHASH\nKHASI\nKHATA\nKHATS\nKHAYA\nKHAZI\nKHEER\nKHELS\nKHENE\nKHENS\nKHETH\nKHETS\nKHIMS\nKHIOS\nKHIPU\nKHIVA\nKHLOE\nKHMER\nKHNUM\nKHOJA\nKHOND\nKHONG\nKHORS\nKHOUM\nKHOYA\nKHUBZ\nKHUDS\nKHUFU\nKHULA\nKHUMS\nKHUUS\nKIAAT\nKIACK\nKIAIS\nKIANA\nKIANG\nKIARA\nKIASI\nKIASU\nKIATO\nKIAWE\nKIBBE\nKIBBY\nKIBED\nKIBEI\nKIBES\nKIBLA\nKIBOR\nKIBUN\nKICKS\nKICKT\nKICKY\nKIDDO\nKIDDY\nKIDEL\nKIDLY\nKIDON\nKIEHL\nKIEHN\nKIELY\nKIERA\nKIERS\nKIEUS\nKIEVE\nKIEVS\nKIFER\nKIGER\nKIGHT\nKIGOS\nKIHNU\nKIKAI\nKIKAR\nKIKAY\nKIKER\nKIKES\nKIKIS\nKIKOI\nKILAS\nKILBY\nKILES\nKILEY\nKILGO\nKILIG\nKILIJ\nKILIM\nKILLS\nKILNS\nKILOS\nKILPE\nKILTS\nKIMBA\nKIMBU\nKIMES\nKIMIE\nKIMMY\nKIMRY\nKIMYE\nKINAH\nKINAS\nKINCH\nKINDA\nKINDE\nKINDS\nKINDT\nKINDY\nKINER\nKINES\nKINGA\nKINGE\nKINGS\nKINGY\nKINIC\nKININ\nKINIT\nKINJO\nKINKI\nKINKS\nKINKY\nKINLY\nKINOK\nKINOO\nKINOS\nKINOT\nKINQU\nKINTZ\nKIOEA\nKIORE\nKIOSK\nKIOTO\nKIOWA\nKIPAH\nKIPAS\nKIPED\nKIPER\nKIPES\nKIPOT\nKIPPA\nKIPPS\nKIPSY\nKIPUS\nKIPUT\nKIRAN\nKIRAT\nKIRBY\nKIRIN\nKIRKE\nKIRKS\nKIRNS\nKIRON\nKIROV\nKIRST\nKIRUV\nKISEL\nKISER\nKISHI\nKISIR\nKISOR\nKISRA\nKISSY\nKISTS\nKITAI\nKITAN\nKITED\nKITEE\nKITER\nKITES\nKITFO\nKITHE\nKITHS\nKITON\nKITTS\nKITTY\nKIVAS\nKIVER\nKIVES\nKIWIS\nKIXES\nKIYIS\nKIZER\nKIZZY\nKKKER\nKLADD\nKLAHN\nKLANG\nKLANN\nKLAPA\nKLAPS\nKLARS\nKLATT\nKLAUS\nKLEIN\nKLEMM\nKLEMP\nKLEMS\nKLENK\nKLICK\nKLIEG\nKLIER\nKLIKS\nKLIMP\nKLIMS\nKLINE\nKLING\nKLINK\nKLITE\nKLOCK\nKLOCS\nKLOMP\nKLONG\nKLOOF\nKLOPP\nKLOSE\nKLUCK\nKLUDD\nKLUGE\nKLUGS\nKLUMB\nKLUMP\nKLUNK\nKLUTH\nKLUTZ\nKMERS\nKMETS\nKMETZ\nKMHMU\nKMIEC\nKNAAK\nKNABS\nKNACK\nKNAGG\nKNAGS\nKNAPE\nKNAPP\nKNAPS\nKNARE\nKNARL\nKNARR\nKNARS\nKNAUB\nKNAUE\nKNAUR\nKNAVE\nKNAWN\nKNAWS\nKNEAD\nKNECK\nKNEED\nKNEEL\nKNEEN\nKNEES\nKNELL\nKNELT\nKNEPP\nKNERR\nKNIBS\nKNICK\nKNIES\nKNIFE\nKNIPE\nKNIPP\nKNISH\nKNITS\nKNIVE\nKNOBS\nKNOCK\nKNODE\nKNOKE\nKNOLL\nKNOPS\nKNORK\nKNORR\nKNORS\nKNOSP\nKNOTS\nKNOTT\nKNOUD\nKNOUT\nKNOWE\nKNOWN\nKNOWS\nKNUBS\nKNUFF\nKNUPP\nKNURD\nKNURL\nKNURR\nKNURS\nKNUTE\nKNUTH\nKNUTS\nKNYFE\nKOALA\nKOANS\nKOBAN\nKOBAS\nKOBER\nKOBON\nKOBOS\nKOBYZ\nKOBZA\nKOCAY\nKOCHI\nKOCKS\nKODAK\nKODOR\nKOEHL\nKOEHN\nKOELS\nKOEPP\nKOFFS\nKOFTA\nKOFTE\nKOFUN\nKOGAL\nKOGAN\nKOGAS\nKOGER\nKOGUT\nKOHAI\nKOHAN\nKOHAS\nKOHEI\nKOHEN\nKOHLI\nKOHLS\nKOHNS\nKOHRS\nKOHUT\nKOINE\nKOJAC\nKOKAS\nKOKAY\nKOKEN\nKOKER\nKOKIS\nKOKRA\nKOKUM\nKOKUS\nKOKYO\nKOLAK\nKOLAM\nKOLAR\nKOLAS\nKOLBE\nKOLBS\nKOLBY\nKOLEA\nKOLEY\nKOLKS\nKOLOA\nKOLOB\nKOLOS\nKOLPS\nKOMAR\nKOMBI\nKOMBU\nKOMIS\nKOMKU\nKOMUZ\nKONAK\nKONBU\nKONDA\nKONDE\nKONDO\nKONEN\nKONES\nKONGO\nKONGS\nKONIG\nKONKS\nKONOS\nKONPA\nKONYA\nKONZE\nKONZO\nKOOBS\nKOOKA\nKOOKS\nKOOKY\nKOOPS\nKOORD\nKOORI\nKOOTS\nKOPEC\nKOPEK\nKOPER\nKOPHS\nKOPIS\nKOPJE\nKOPKA\nKOPPA\nKOPUZ\nKORAI\nKORAN\nKORAS\nKORAT\nKOREA\nKORES\nKOREY\nKORFF\nKORFS\nKORIN\nKORIS\nKORLA\nKORMA\nKOROR\nKORPI\nKORSI\nKORTE\nKORTH\nKORTS\nKORTZ\nKORUN\nKORUS\nKOSAR\nKOSEK\nKOSER\nKOSHA\nKOSHY\nKOSIK\nKOSKA\nKOSKO\nKOTAS\nKOTEL\nKOTHE\nKOTHS\nKOTKA\nKOTOE\nKOTOR\nKOTOS\nKOTOW\nKOTTE\nKOTTU\nKOUBA\nKOURA\nKOURI\nKOURY\nKOUSE\nKOVAC\nKOVAL\nKOVAR\nKOVIL\nKOVSH\nKOWAL\nKOWAN\nKOYOK\nKOZAR\nKOZAS\nKOZEL\nKOZIK\nKOZUE\nKRAAL\nKRAAR\nKRABI\nKRABS\nKRAFT\nKRAHN\nKRAIS\nKRAIT\nKRALS\nKRAMA\nKRANG\nKRANS\nKRANZ\nKRAPF\nKRARS\nKRATT\nKRATZ\nKRAUL\nKRAUT\nKRAYS\nKREBS\nKREEL\nKREEP\nKREES\nKREFT\nKREIN\nKRELL\nKRENG\nKRENZ\nKRETZ\nKREWE\nKREYS\nKRIEK\nKRIER\nKRIGE\nKRILL\nKRING\nKRIOL\nKRISA\nKRISH\nKRIYA\nKRNOV\nKROHN\nKROHS\nKROLL\nKROLS\nKRONA\nKRONE\nKRONI\nKRONK\nKRONS\nKROON\nKROOS\nKROUT\nKRSAN\nKRSNA\nKRUBI\nKRUFT\nKRULL\nKRUMM\nKRUMP\nKRUNK\nKRUPA\nKSARS\nKSERS\nKSLOC\nKSOUR\nKTULU\nKUANG\nKUANS\nKUBAN\nKUBAT\nKUBBA\nKUBER\nKUBES\nKUBIE\nKUBIK\nKUCHA\nKUCKS\nKUDAS\nKUDER\nKUDLA\nKUDOS\nKUDUS\nKUDZU\nKUEHL\nKUEHN\nKUFFS\nKUFIC\nKUFIS\nKUFTA\nKUGEL\nKUHAR\nKUHLS\nKUHMO\nKUHRS\nKUKIS\nKUKLA\nKUKRI\nKUKUI\nKULAK\nKULAN\nKULAS\nKULFI\nKULIG\nKULIK\nKULIS\nKULKA\nKULLS\nKULOW\nKULPA\nKULPS\nKUMAN\nKUMAR\nKUMDO\nKUMIS\nKUMMS\nKUMPF\nKUMPS\nKUMST\nKUMUX\nKUMYK\nKUMYS\nKUNAI\nKUNAS\nKUNCE\nKUNES\nKUNIK\nKUNJU\nKUNQU\nKUNTI\nKUNTZ\nKUNYA\nKUNZE\nKUPER\nKUPKA\nKURAN\nKURDS\nKUREK\nKURIA\nKURMA\nKURSK\nKURTA\nKURTH\nKURTI\nKURTZ\nKURUS\nKUSEK\nKUTAS\nKUTCH\nKUTUM\nKUYAS\nKUZMA\nKVASS\nKVELL\nKVENS\nKVITL\nKWAAI\nKWAKS\nKWAME\nKWANS\nKWASO\nKWAZA\nKWEEN\nKWELA\nKWENG\nKWERE\nKWICS\nKWISE\nKWONG\nKWONS\nKYACK\nKYAKS\nKYARN\nKYATS\nKYAWS\nKYBOS\nKYDST\nKYERS\nKYKED\nKYKER\nKYLAH\nKYLAN\nKYLEE\nKYLEN\nKYLER\nKYLES\nKYLEY\nKYLIE\nKYLIX\nKYLOE\nKYMRY\nKYMYS\nKYMYZ\nKYNDE\nKYNDS\nKYNGE\nKYNGS\nKYOKO\nKYOTO\nKYPES\nKYREE\nKYRIE\nKYSAR\nKYSER\nKYSON\nKYTAY\nKYTES\nKYTHE\nKYTLE\nKYUDO\nKYZER\nKYZYL\nLAABS\nLAAMS\nLAARI\nLABAN\nLABAR\nLABAT\nLABBA\nLABBE\nLABDA\nLABEL\nLABER\nLABES\nLABIA\nLABIN\nLABIS\nLABOR\nLABOY\nLABRA\nLACED\nLACER\nLACES\nLACEY\nLACID\nLACIS\nLACKE\nLACKS\nLACKY\nLADAS\nLADDS\nLADDU\nLADDY\nLADED\nLADEN\nLADER\nLADES\nLADIE\nLADIN\nLADLE\nLADSY\nLADUE\nLADYE\nLAFFS\nLAFON\nLAGAN\nLAGER\nLAGGY\nLAGID\nLAGOS\nLAHAR\nLAHEY\nLAHMU\nLAHOH\nLAHRS\nLAHTI\nLAHUE\nLAICK\nLAICS\nLAIKS\nLAILA\nLAILS\nLAINE\nLAING\nLAINO\nLAINS\nLAIRD\nLAIRS\nLAIRY\nLAITH\nLAITS\nLAITY\nLAIUS\nLAKAO\nLAKED\nLAKER\nLAKES\nLAKEY\nLAKHA\nLAKHS\nLAKIN\nLAKKA\nLAKOU\nLAKSA\nLALLA\nLALLI\nLALLS\nLALLY\nLALOR\nLAMAN\nLAMAR\nLAMAS\nLAMAY\nLAMBA\nLAMBE\nLAMBO\nLAMBS\nLAMBY\nLAMDA\nLAMED\nLAMEL\nLAMEN\nLAMEO\nLAMER\nLAMES\nLAMEY\nLAMIA\nLAMIE\nLAMIN\nLAMMY\nLAMPP\nLAMPS\nLAMYS\nLANAI\nLANAO\nLANAP\nLANCE\nLANCH\nLANCS\nLANDA\nLANDE\nLANDI\nLANDS\nLANDY\nLANED\nLANER\nLANES\nLANEY\nLANGI\nLANGS\nLANKA\nLANKS\nLANKY\nLANNI\nLANTS\nLANTZ\nLANUM\nLANZA\nLAOIS\nLAOSI\nLAOYA\nLAOZI\nLAPAN\nLAPAS\nLAPEL\nLAPES\nLAPIN\nLAPPA\nLAPPS\nLAPPY\nLAPSE\nLAPTI\nLAPUA\nLARBS\nLARCH\nLARDO\nLARDS\nLARDY\nLARED\nLAREN\nLARES\nLARFS\nLARGE\nLARGO\nLARGS\nLARID\nLARIN\nLARIS\nLARKS\nLARKY\nLARNS\nLARNT\nLAROE\nLARPS\nLARRY\nLARTS\nLARUE\nLARUM\nLARVA\nLARVE\nLARYS\nLASED\nLASEK\nLASER\nLASES\nLASHA\nLASHI\nLASHT\nLASIK\nLASIX\nLASKA\nLASKO\nLASKS\nLASKY\nLASSI\nLASSO\nLASSU\nLASSY\nLASTE\nLASTS\nLATAH\nLATCH\nLATED\nLATEN\nLATER\nLATES\nLATEX\nLATHE\nLATHI\nLATHS\nLATHY\nLATIC\nLATID\nLATIF\nLATIK\nLATIN\nLATKA\nLATKE\nLATON\nLATTA\nLATTE\nLATUS\nLAUAN\nLAUCK\nLAUDS\nLAUER\nLAUES\nLAUGH\nLAUJE\nLAUND\nLAURA\nLAURI\nLAURO\nLAURY\nLAUTH\nLAVAL\nLAVAN\nLAVAS\nLAVED\nLAVER\nLAVES\nLAVEY\nLAVIC\nLAVIN\nLAVOY\nLAVRA\nLAVVY\nLAVYS\nLAWAL\nLAWDY\nLAWED\nLAWES\nLAWKS\nLAWLY\nLAWND\nLAWNS\nLAWNY\nLAWRO\nLAWRY\nLAXED\nLAXEN\nLAXER\nLAXES\nLAXEY\nLAXLY\nLAXMI\nLAYAN\nLAYBY\nLAYED\nLAYER\nLAYES\nLAYIN\nLAYLA\nLAYNE\nLAYUP\nLAZAR\nLAZED\nLAZEN\nLAZER\nLAZES\nLAZIO\nLAZOR\nLAZOS\nLAZZI\nLAZZO\nLBFMS\nLBFTS\nLBSCR\nLCACS\nLCCNS\nLCTLS\nLDIFS\nLEACH\nLEADE\nLEADS\nLEADY\nLEAFS\nLEAFY\nLEAGS\nLEAHY\nLEAKE\nLEAKS\nLEAKY\nLEAMS\nLEANE\nLEANO\nLEANS\nLEANT\nLEANY\nLEAPS\nLEAPT\nLEARD\nLEARN\nLEARS\nLEARY\nLEASE\nLEASH\nLEAST\nLEASY\nLEATH\nLEATS\nLEAUE\nLEAVE\nLEAVY\nLEAZE\nLEBAN\nLEBBO\nLEBEL\nLEBES\nLEBNI\nLEBOS\nLEBOW\nLECCE\nLECCO\nLECCY\nLECHE\nLECHI\nLECHS\nLECHY\nLECKY\nLECTS\nLEDAY\nLEDCS\nLEDDY\nLEDEN\nLEDES\nLEDET\nLEDGE\nLEDGY\nLEDOS\nLEDUC\nLEECH\nLEEDS\nLEEDY\nLEEKS\nLEEKY\nLEELA\nLEEMS\nLEENS\nLEEPS\nLEERE\nLEERS\nLEERY\nLEESE\nLEESY\nLEETH\nLEETS\nLEEZA\nLEFFS\nLEFSA\nLEFSE\nLEFTE\nLEFTS\nLEFTY\nLEFUL\nLEGAL\nLEGAN\nLEGAS\nLEGCO\nLEGED\nLEGER\nLEGES\nLEGGE\nLEGGO\nLEGGY\nLEGIT\nLEGOS\nLEHAN\nLEHEW\nLEHNS\nLEHRS\nLEHUA\nLEIBY\nLEICS\nLEIDS\nLEIDY\nLEIGH\nLEIHE\nLEIJA\nLEILA\nLEINE\nLEINT\nLEISH\nLEIST\nLEITE\nLEITH\nLEITZ\nLEIVA\nLEJAS\nLELLA\nLEMAN\nLEMAR\nLEMAS\nLEMAY\nLEMBO\nLEMEL\nLEMEN\nLEMER\nLEMHI\nLEMKE\nLEMKO\nLEMMA\nLEMME\nLEMMS\nLEMMY\nLEMON\nLEMUR\nLEMUS\nLENDS\nLENES\nLENGA\nLENGS\nLENIN\nLENIS\nLENNS\nLENNY\nLENOI\nLENON\nLENOS\nLENOX\nLENSE\nLENTO\nLENTS\nLENTZ\nLENZI\nLEOIS\nLEOLA\nLEONA\nLEONE\nLEONG\nLEORA\nLEOTI\nLEPAK\nLEPAL\nLEPAS\nLEPEL\nLEPER\nLEPES\nLEPID\nLEPOS\nLEPPS\nLEPPY\nLEPRA\nLEPRY\nLEPTA\nLEPTO\nLEPUS\nLERCH\nLERKY\nLERMA\nLEROI\nLEROY\nLERPS\nLESBI\nLESBO\nLESBY\nLESES\nLESHY\nLESKE\nLESKO\nLESKS\nLESSE\nLETCH\nLETHE\nLETHY\nLETTS\nLETTY\nLETUP\nLEUCK\nLEUDS\nLEUNG\nLEVAN\nLEVAS\nLEVEE\nLEVEL\nLEVEN\nLEVER\nLEVET\nLEVEY\nLEVIN\nLEVIR\nLEVIS\nLEVIT\nLEWED\nLEWER\nLEWES\nLEWIN\nLEWIS\nLEWTH\nLEXAS\nLEXED\nLEXER\nLEXES\nLEXIA\nLEXIC\nLEXIE\nLEXIS\nLEXIT\nLEXON\nLEYAK\nLEYBA\nLEYNS\nLEYTE\nLEYVA\nLEZBO\nLEZGI\nLEZZA\nLEZZO\nLEZZY\nLFSRS\nLGATS\nLGBTA\nLGBTI\nLGBTQ\nLGBTS\nLHASA\nLHRNA\nLIAGE\nLIALH\nLIANA\nLIANE\nLIANG\nLIANS\nLIARD\nLIARS\nLIASE\nLIBBY\nLIBEL\nLIBER\nLIBOR\nLIBRA\nLIBRE\nLIBRY\nLIBYA\nLIBYC\nLICCA\nLICEA\nLICEY\nLICHI\nLICIT\nLICKS\nLICKT\nLICKY\nLICON\nLIDAR\nLIDDY\nLIDGE\nLIDIA\nLIDOL\nLIDOS\nLIEBL\nLIEBS\nLIEGE\nLIEMS\nLIENS\nLIERA\nLIERS\nLIEST\nLIETH\nLIETZ\nLIEUS\nLIEWS\nLIFEN\nLIFER\nLIFES\nLIFEY\nLIFFE\nLIFIE\nLIFTS\nLIGAF\nLIGAN\nLIGAS\nLIGED\nLIGER\nLIGHT\nLIGON\nLIHOU\nLIHUE\nLIKAM\nLIKED\nLIKEE\nLIKEN\nLIKER\nLIKES\nLIKEY\nLIKIN\nLIKOU\nLIKUD\nLILAC\nLILAH\nLILES\nLILIA\nLILLE\nLILLS\nLILLY\nLILOS\nLILTS\nLIMAN\nLIMAS\nLIMBA\nLIMBI\nLIMBO\nLIMBS\nLIMBU\nLIMBY\nLIMED\nLIMEN\nLIMER\nLIMES\nLIMEY\nLIMID\nLIMIT\nLIMMA\nLIMMU\nLIMNS\nLIMON\nLIMOS\nLIMPA\nLIMPS\nLIMUS\nLINAC\nLINAM\nLINAN\nLINCH\nLINCK\nLINCS\nLINDA\nLINDO\nLINDS\nLINDY\nLINEA\nLINED\nLINEL\nLINEN\nLINER\nLINES\nLINGA\nLINGE\nLINGO\nLINGS\nLINGY\nLININ\nLINKS\nLINKT\nLINKY\nLINNA\nLINNE\nLINNS\nLINNY\nLINOS\nLINTS\nLINTY\nLINTZ\nLINUS\nLINUX\nLINYI\nLINZY\nLIONS\nLIOUS\nLIPAN\nLIPAS\nLIPES\nLIPIC\nLIPID\nLIPIK\nLIPIN\nLIPKA\nLIPKE\nLIPPS\nLIPPY\nLIPYL\nLIRAE\nLIRAS\nLIRES\nLIRGS\nLIRKS\nLIROT\nLIRTS\nLISIS\nLISKA\nLISKS\nLISLE\nLISNE\nLISPS\nLISPY\nLISSE\nLISSY\nLISTS\nLISTY\nLISZT\nLITAI\nLITAS\nLITED\nLITER\nLITES\nLITHA\nLITHE\nLITHO\nLITHP\nLITHS\nLITHY\nLITKE\nLITRA\nLITRE\nLITTS\nLITTY\nLITUI\nLIULI\nLIVED\nLIVEN\nLIVER\nLIVES\nLIVIA\nLIVID\nLIVIN\nLIVOR\nLIVRE\nLIVVI\nLIVVY\nLIXIN\nLIZAS\nLIZZY\nLJBFS\nLJERS\nLKDNA\nLLAMA\nLLANI\nLLANO\nLLETZ\nLLOYD\nLLSVP\nLMBAO\nLMFAO\nLMHCS\nLMRNA\nLMTVS\nLMWAO\nLMWDS\nLMXBS\nLNWIS\nLOACH\nLOADS\nLOAEL\nLOAFS\nLOAMS\nLOAMY\nLOANS\nLOARS\nLOATH\nLOAVE\nLOBAL\nLOBAR\nLOBBS\nLOBBY\nLOBED\nLOBEL\nLOBES\nLOBOR\nLOBOS\nLOBUE\nLOBUS\nLOCAL\nLOCAO\nLOCAS\nLOCHE\nLOCHS\nLOCIN\nLOCKE\nLOCKS\nLOCKT\nLOCKY\nLOCON\nLOCOS\nLOCUM\nLOCUS\nLODDE\nLODEN\nLODER\nLODES\nLODGE\nLOEHR\nLOERA\nLOESS\nLOEWE\nLOEZA\nLOFER\nLOFTS\nLOFTY\nLOGAN\nLOGES\nLOGGE\nLOGGY\nLOGIA\nLOGIC\nLOGIN\nLOGIT\nLOGKO\nLOGOI\nLOGON\nLOGOS\nLOGUE\nLOHAN\nLOHAS\nLOHRS\nLOHSE\nLOIAL\nLOIDS\nLOIKE\nLOINC\nLOING\nLOINS\nLOIPE\nLOIRE\nLOIRS\nLOKAO\nLOKEN\nLOKES\nLOKEY\nLOKMA\nLOKUN\nLOLED\nLOLER\nLOLIS\nLOLLS\nLOLLY\nLOLOL\nLOLOT\nLOMAN\nLOMAS\nLOMAX\nLOMID\nLOMLS\nLONAS\nLONCO\nLONER\nLONEY\nLONGA\nLONGE\nLONGO\nLONGS\nLONGY\nLONKO\nLONON\nLOOBS\nLOOBY\nLOOCH\nLOOED\nLOOEY\nLOOFA\nLOOFS\nLOOGY\nLOOIE\nLOOKE\nLOOKS\nLOOKT\nLOOKY\nLOOMS\nLOONG\nLOONS\nLOONY\nLOOPS\nLOOPY\nLOORD\nLOORS\nLOOSE\nLOOSH\nLOOTS\nLOPED\nLOPER\nLOPES\nLOPEZ\nLOPHS\nLOPPS\nLOPPY\nLORAH\nLORAL\nLORAN\nLORDS\nLORDY\nLORED\nLOREE\nLOREL\nLOREN\nLORES\nLORIA\nLORID\nLORIE\nLORIO\nLORIS\nLORKS\nLORNA\nLORRA\nLORRI\nLORRY\nLOSAR\nLOSED\nLOSEE\nLOSEL\nLOSER\nLOSES\nLOSEY\nLOSSY\nLOTAG\nLOTAH\nLOTAS\nLOTED\nLOTES\nLOTHE\nLOTHS\nLOTIC\nLOTID\nLOTIS\nLOTOS\nLOTSA\nLOTTA\nLOTTE\nLOTTO\nLOTTS\nLOTUS\nLOUBS\nLOUDE\nLOUER\nLOUES\nLOUGH\nLOUIE\nLOUIS\nLOUKS\nLOUNS\nLOUPE\nLOUPS\nLOURI\nLOURS\nLOURY\nLOUSE\nLOUSY\nLOUTH\nLOUTS\nLOUTY\nLOUVE\nLOVAN\nLOVAS\nLOVAT\nLOVED\nLOVEE\nLOVEL\nLOVEN\nLOVER\nLOVES\nLOVEY\nLOVEZ\nLOVIE\nLOVIN\nLOVOS\nLOWED\nLOWER\nLOWES\nLOWLY\nLOWNE\nLOWNS\nLOWRY\nLOWTH\nLOWYS\nLOXIA\nLOYAL\nLOYAS\nLOYCE\nLOYDS\nLOZAS\nLOZEL\nLPERS\nLPWAN\nLRAAM\nLRADS\nLRASM\nLRBMS\nLRRPS\nLSFES\nLSFMS\nLSSAH\nLTCOL\nLUANA\nLUANN\nLUANS\nLUAUS\nLUBEC\nLUBED\nLUBER\nLUBES\nLUBIN\nLUBKI\nLUBOK\nLUBRA\nLUBYS\nLUCAN\nLUCAS\nLUCCA\nLUCCI\nLUCEA\nLUCES\nLUCET\nLUCEY\nLUCHI\nLUCIA\nLUCID\nLUCKE\nLUCKS\nLUCKY\nLUCMO\nLUCRE\nLUDDY\nLUDES\nLUDIC\nLUDOS\nLUDYS\nLUECK\nLUERA\nLUETH\nLUFFA\nLUFFS\nLUFTY\nLUGAR\nLUGED\nLUGER\nLUGES\nLUGOJ\nLUHYA\nLUING\nLUISI\nLUJAN\nLUJVO\nLUKAC\nLUKAN\nLUKAS\nLUKER\nLUKES\nLUKIS\nLULAV\nLULEA\nLULES\nLULLA\nLULLS\nLULLY\nLULUS\nLULZY\nLUMAD\nLUMAN\nLUMAS\nLUMEN\nLUMIA\nLUMIC\nLUMME\nLUMMY\nLUMPS\nLUMPY\nLUNAR\nLUNAS\nLUNCH\nLUNDA\nLUNDY\nLUNEL\nLUNES\nLUNET\nLUNGE\nLUNGI\nLUNGO\nLUNGS\nLUNGU\nLUNKS\nLUNKY\nLUNTS\nLUNYU\nLUOHU\nLUONG\nLUPER\nLUPIN\nLUPPA\nLUPUS\nLUQUE\nLURAY\nLURCH\nLURED\nLURER\nLURES\nLUREX\nLURGI\nLURGY\nLURID\nLURIE\nLURKS\nLURKY\nLURRY\nLURTS\nLURTY\nLURVE\nLUSBY\nLUSCA\nLUSER\nLUSES\nLUSHY\nLUSKS\nLUSTS\nLUSTY\nLUTAR\nLUTEA\nLUTED\nLUTER\nLUTES\nLUTHI\nLUTHS\nLUTHY\nLUTON\nLUTSK\nLUVAR\nLUVVY\nLUXED\nLUXES\nLUXON\nLUXOR\nLUZON\nLUZZI\nLUZZU\nLVMPD\nLWALU\nLWEIS\nLWINS\nLWUIT\nLYALL\nLYAMS\nLYARS\nLYASE\nLYATE\nLYBIA\nLYCEE\nLYCIA\nLYCID\nLYCRA\nLYDAY\nLYDEN\nLYDES\nLYDIA\nLYDIC\nLYDON\nLYEGE\nLYELL\nLYEST\nLYETH\nLYING\nLYKES\nLYLAB\nLYLAH\nLYLAS\nLYLES\nLYMAN\nLYMED\nLYMER\nLYMES\nLYMON\nLYMPH\nLYNAM\nLYNCH\nLYNDA\nLYNDE\nLYNDS\nLYNES\nLYNKS\nLYNNE\nLYONS\nLYRAS\nLYRES\nLYRIC\nLYRID\nLYRIE\nLYSED\nLYSES\nLYSIN\nLYSIS\nLYSOL\nLYSSA\nLYSYL\nLYTHE\nLYTIC\nLYTLE\nLYTTA\nLYVES\nLYZED\nLYZES\nMAAAN\nMAACK\nMAAED\nMAAFA\nMAARE\nMAARS\nMAATS\nMABBE\nMABBY\nMABEE\nMABEL\nMABEN\nMABES\nMABEY\nMABIA\nMABIE\nMABLE\nMABON\nMABRY\nMABYE\nMACAO\nMACAU\nMACAW\nMACCA\nMACCO\nMACED\nMACER\nMACES\nMACEY\nMACHA\nMACHE\nMACHI\nMACHO\nMACIE\nMACIN\nMACKS\nMACKY\nMACLE\nMACON\nMACOS\nMACRA\nMACRO\nMADAM\nMADAN\nMADAR\nMADAY\nMADDA\nMADDY\nMADEJ\nMADER\nMADES\nMADGE\nMADID\nMADLY\nMADRY\nMAEDA\nMAEDI\nMAERL\nMAESE\nMAEVE\nMAFFS\nMAFIA\nMAFIC\nMAFOO\nMAGAN\nMAGAR\nMAGAS\nMAGAT\nMAGBY\nMAGEE\nMAGER\nMAGES\nMAGGI\nMAGHA\nMAGIC\nMAGID\nMAGIN\nMAGMA\nMAGNA\nMAGOG\nMAGOT\nMAGRI\nMAGRO\nMAGTF\nMAGUS\nMAGWE\nMAHAL\nMAHAN\nMAHAR\nMAHDI\nMAHER\nMAHEU\nMAHLI\nMAHOE\nMAHON\nMAHRS\nMAHUA\nMAHWA\nMAIDA\nMAIDE\nMAIDS\nMAIDU\nMAIKA\nMAIKO\nMAILE\nMAILO\nMAILS\nMAILY\nMAIMS\nMAINA\nMAINE\nMAINS\nMAINZ\nMAIRE\nMAIST\nMAISY\nMAIZE\nMAJAT\nMAJEL\nMAJID\nMAJKA\nMAJOR\nMAJUN\nMAJUR\nMAKAH\nMAKAI\nMAKAR\nMAKED\nMAKEE\nMAKEM\nMAKEN\nMAKER\nMAKES\nMAKHI\nMAKIN\nMAKIS\nMAKOS\nMAKUA\nMAKWA\nMALAI\nMALAK\nMALAM\nMALAR\nMALAS\nMALAX\nMALAY\nMALDA\nMALDI\nMALEC\nMALEK\nMALEO\nMALER\nMALES\nMALEY\nMALHI\nMALIA\nMALIC\nMALIK\nMALIN\nMALIS\nMALKY\nMALLE\nMALLO\nMALLS\nMALLU\nMALMO\nMALMS\nMALOS\nMALOY\nMALTA\nMALTS\nMALTY\nMALTZ\nMALUM\nMALUS\nMALVA\nMALVI\nMALWA\nMALYS\nMAMAK\nMAMAS\nMAMAW\nMAMBA\nMAMBO\nMAMEY\nMAMIE\nMAMIL\nMAMMA\nMAMMY\nMAMOS\nMAMPY\nMAMSY\nMAMTA\nMANAL\nMANAM\nMANAS\nMANAT\nMANCA\nMANCE\nMANCS\nMANDA\nMANDE\nMANDI\nMANDO\nMANDS\nMANDU\nMANDY\nMANEB\nMANED\nMANEH\nMANER\nMANES\nMANET\nMANEY\nMANGA\nMANGE\nMANGO\nMANGS\nMANGU\nMANGY\nMANIA\nMANIC\nMANID\nMANIP\nMANIS\nMANIX\nMANJI\nMANJO\nMANJU\nMANKA\nMANKE\nMANKS\nMANKY\nMANLY\nMANNA\nMANNY\nMANOR\nMANOS\nMANRY\nMANSA\nMANSE\nMANSI\nMANSO\nMANTA\nMANTI\nMANTO\nMANTY\nMANTZ\nMANUL\nMANUS\nMANYE\nMANZI\nMANZO\nMAOHI\nMAOLI\nMAORE\nMAORI\nMAPAU\nMAPLE\nMAPLY\nMAPOU\nMAQAF\nMAQAM\nMAQTA\nMAQUI\nMARAE\nMARAH\nMARAI\nMARAJ\nMARAS\nMARAW\nMARAY\nMARCH\nMARCO\nMARCS\nMARCY\nMARDI\nMARDS\nMARDY\nMAREN\nMARES\nMAREZ\nMARFA\nMARGA\nMARGE\nMARGO\nMARGS\nMARIA\nMARIE\nMARIL\nMARIN\nMARIO\nMARIS\nMARKE\nMARKO\nMARKS\nMARKT\nMARKY\nMARLA\nMARLE\nMARLS\nMARLY\nMARMA\nMARMO\nMARMS\nMARNE\nMAROG\nMARON\nMAROR\nMARRA\nMARRI\nMARRO\nMARRY\nMARSE\nMARSH\nMARTA\nMARTH\nMARTS\nMARTY\nMARTZ\nMARUA\nMARVS\nMARVY\nMARYS\nMASAI\nMASAM\nMASAO\nMASCI\nMASEK\nMASER\nMASES\nMASHA\nMASHY\nMASIH\nMASIS\nMASKS\nMASON\nMASRI\nMASSA\nMASSE\nMASSI\nMASSO\nMASSY\nMASTA\nMASTS\nMASTY\nMASYU\nMATAI\nMATAM\nMATAR\nMATCH\nMATED\nMATEI\nMATER\nMATES\nMATEY\nMATHA\nMATHO\nMATHS\nMATHY\nMATIE\nMATIN\nMATJE\nMATKI\nMATOS\nMATRA\nMATSU\nMATTA\nMATTE\nMATTY\nMATZA\nMATZO\nMAUBY\nMAUCH\nMAUCK\nMAUDE\nMAUDS\nMAUKA\nMAUKS\nMAULE\nMAULL\nMAULS\nMAUMA\nMAUMS\nMAUMY\nMAUND\nMAUNG\nMAURA\nMAURI\nMAURY\nMAUSS\nMAUST\nMAUVE\nMAUVY\nMAUZA\nMAUZY\nMAVEN\nMAVIN\nMAVIS\nMAWAN\nMAWED\nMAWKS\nMAWKY\nMAWLA\nMAWLE\nMAWNS\nMAXAM\nMAXED\nMAXES\nMAXEY\nMAXIE\nMAXIM\nMAXIS\nMAXON\nMAYAN\nMAYAS\nMAYBE\nMAYED\nMAYEN\nMAYER\nMAYES\nMAYLE\nMAYME\nMAYNE\nMAYON\nMAYOR\nMAYOS\nMAYRS\nMAYSE\nMAYST\nMAZAK\nMAZAS\nMAZDA\nMAZED\nMAZER\nMAZES\nMAZEY\nMAZON\nMAZOS\nMAZUT\nMAZZA\nMBARI\nMBBCH\nMBILA\nMBIRA\nMBITS\nMBOGA\nMBOUR\nMBRET\nMBUBE\nMBUGA\nMBUNA\nMBYTE\nMCATS\nMCBEE\nMCCAR\nMCCAW\nMCCAY\nMCGAW\nMCGEE\nMCHEM\nMCING\nMCJOB\nMCKAY\nMCKIE\nMCNEW\nMCPON\nMCRAE\nMCRIT\nMCSES\nMCTFS\nMDINA\nMDNAS\nMEACH\nMEACO\nMEADE\nMEADS\nMEADY\nMEAKS\nMEALS\nMEALY\nMEANE\nMEANS\nMEANT\nMEANY\nMEARE\nMEARS\nMEASE\nMEATH\nMEATI\nMEATS\nMEATY\nMEAUX\nMEAWL\nMEAWS\nMEBBE\nMEBBY\nMEBIN\nMEBOS\nMECCA\nMECHA\nMECHE\nMECHS\nMECKS\nMECON\nMECOS\nMECUM\nMEDAK\nMEDAL\nMEDAN\nMEDAR\nMEDCS\nMEDDY\nMEDEA\nMEDEL\nMEDEN\nMEDER\nMEDES\nMEDEX\nMEDIA\nMEDIC\nMEDII\nMEDLI\nMEDLY\nMEDOC\nMEECE\nMEECH\nMEEDS\nMEEJA\nMEEKS\nMEEMS\nMEEPS\nMEERS\nMEESE\nMEESS\nMEETE\nMEETS\nMEFFS\nMEFOS\nMEGAM\nMEGAN\nMEGAW\nMEGNA\nMEHAN\nMEHDI\nMEHRA\nMEHTA\nMEIDE\nMEIDO\nMEIGS\nMEIJI\nMEIKO\nMEINY\nMEISM\nMEJIA\nMEKHI\nMEKIN\nMEKON\nMELAM\nMELAN\nMELAS\nMELBA\nMELBY\nMELDS\nMELEE\nMELEM\nMELES\nMELFI\nMELIC\nMELID\nMELIN\nMELLO\nMELLS\nMELON\nMELOS\nMELOY\nMELTS\nMELTY\nMEMAW\nMEMED\nMEMEL\nMEMER\nMEMES\nMEMEX\nMEMIC\nMEMMS\nMEMON\nMEMOS\nMENAE\nMENDE\nMENDS\nMENDY\nMENGS\nMENID\nMENKE\nMENLO\nMENNE\nMENNO\nMENON\nMENOR\nMENOW\nMENPO\nMENSA\nMENSE\nMENSK\nMENTA\nMENTO\nMENTZ\nMENUS\nMEOLA\nMEOUS\nMEOWL\nMEOWS\nMEOWY\nMERAL\nMERAZ\nMERBY\nMERCE\nMERCH\nMERCI\nMERCK\nMERCS\nMERCY\nMERDE\nMERED\nMERES\nMERGE\nMERIT\nMERKS\nMERLE\nMERLO\nMERLS\nMERMS\nMERNA\nMEROE\nMEROI\nMERON\nMEROS\nMERRY\nMERSH\nMERTZ\nMERUS\nMERYA\nMERYL\nMESAD\nMESAL\nMESAS\nMESEL\nMESEM\nMESEN\nMESHY\nMESIC\nMESNA\nMESNE\nMESON\nMESOR\nMESSI\nMESSY\nMESTA\nMESTO\nMESYL\nMETAL\nMETAR\nMETAS\nMETED\nMETEG\nMETER\nMETES\nMETHI\nMETHO\nMETHS\nMETHY\nMETIC\nMETIF\nMETIS\nMETOL\nMETOO\nMETRA\nMETRE\nMETRO\nMETTS\nMEUSE\nMEUTE\nMEWED\nMEWER\nMEWLS\nMEWPS\nMEWUK\nMEXXY\nMEYER\nMEYNT\nMEZAS\nMEZES\nMEZZE\nMEZZO\nMFPRS\nMFVSG\nMFWIC\nMFWTK\nMGTOW\nMGUHS\nMGUKS\nMHORR\nMHTML\nMIAMI\nMIANO\nMIAOU\nMIAOW\nMIASM\nMIAUL\nMICAH\nMICAS\nMICEK\nMICKS\nMICKY\nMICOS\nMICRO\nMIDAN\nMIDAS\nMIDAZ\nMIDDX\nMIDDY\nMIDGE\nMIDIR\nMIDIS\nMIDST\nMIEKO\nMIENS\nMIERA\nMIEVE\nMIFFS\nMIFFY\nMIGAS\nMIGGY\nMIGHT\nMIGID\nMIGOD\nMIHIS\nMIHMS\nMIJIU\nMIKAN\nMIKED\nMIKES\nMIKEY\nMIKOS\nMIKOV\nMIKVA\nMIKVE\nMILAB\nMILAM\nMILAN\nMILBY\nMILCH\nMILDS\nMILER\nMILES\nMILEY\nMILFS\nMILIA\nMILKO\nMILKS\nMILKT\nMILKY\nMILLS\nMILLY\nMILNE\nMILOS\nMILPA\nMILSE\nMILTS\nMIMAS\nMIMED\nMIMEO\nMIMER\nMIMES\nMIMIC\nMIMID\nMIMIR\nMIMSY\nMINAC\nMINAE\nMINAH\nMINAS\nMINBU\nMINCE\nMINCH\nMINCO\nMINCY\nMINDA\nMINDE\nMINDS\nMINDY\nMINED\nMINER\nMINES\nMINGA\nMINGE\nMINGO\nMINGS\nMINGW\nMINGY\nMINIM\nMINIS\nMINIX\nMINKE\nMINKS\nMINMI\nMINNY\nMINOR\nMINOS\nMINOT\nMINOW\nMINSE\nMINSK\nMINTO\nMINTS\nMINTY\nMINTZ\nMINUM\nMINUS\nMINXY\nMIPSS\nMIRAA\nMIRED\nMIRES\nMIREX\nMIRID\nMIRIN\nMIRIS\nMIRKY\nMIRNA\nMIRON\nMIRRS\nMIRTH\nMIRVS\nMIRZA\nMISAL\nMISDO\nMISER\nMISES\nMISGO\nMISHA\nMISIN\nMISKO\nMISLE\nMISLS\nMISLY\nMISOS\nMISRA\nMISSA\nMISSY\nMISTS\nMISTY\nMITCH\nMITER\nMITES\nMITEY\nMITIS\nMITRA\nMITRE\nMITTA\nMITTS\nMITTY\nMITZI\nMIURA\nMIWOK\nMIWUK\nMIXED\nMIXEL\nMIXEN\nMIXER\nMIXES\nMIXIE\nMIXIN\nMIXON\nMIXTE\nMIXUP\nMIZAR\nMIZEN\nMIZER\nMIZES\nMIZUE\nMIZZY\nMKEKA\nMLBER\nMLJET\nMLLES\nMLSCN\nMLSER\nMLSES\nMMANI\nMMAPI\nMMATH\nMMBTU\nMMCDS\nMMELS\nMMHMM\nMMIWG\nMMKAY\nMMOLE\nMMOLS\nMNEME\nMNIST\nMNRAS\nMOABI\nMOABS\nMOAIS\nMOAKS\nMOALD\nMOANS\nMOANY\nMOATS\nMOBAD\nMOBAS\nMOBAY\nMOBBY\nMOBED\nMOBES\nMOBEY\nMOBIL\nMOBLE\nMOBOS\nMOBOT\nMOCAP\nMOCCS\nMOCHA\nMOCHE\nMOCHI\nMOCKS\nMOCKT\nMOCOS\nMOCVD\nMODAK\nMODAL\nMODCA\nMODED\nMODEL\nMODEM\nMODER\nMODES\nMODIE\nMODII\nMODOC\nMODON\nMODUS\nMODYS\nMOEKO\nMOELS\nMOERS\nMOESI\nMOFFA\nMOGAN\nMOGAS\nMOGGS\nMOGGY\nMOGOK\nMOGOR\nMOGRA\nMOGUL\nMOHAN\nMOHAR\nMOHEL\nMOHLS\nMOHOS\nMOHRS\nMOHUA\nMOHUR\nMOILE\nMOILS\nMOIRA\nMOIRE\nMOIRS\nMOISE\nMOIST\nMOJOS\nMOJRI\nMOKEN\nMOKES\nMOKOS\nMOLAL\nMOLAP\nMOLAR\nMOLAS\nMOLDE\nMOLDS\nMOLDY\nMOLEK\nMOLES\nMOLID\nMOLIN\nMOLLA\nMOLLE\nMOLLO\nMOLLS\nMOLLY\nMOLOC\nMOLOI\nMOLOK\nMOLTS\nMOLVE\nMOMAN\nMOMES\nMOMIC\nMOMIN\nMOMLY\nMOMMA\nMOMME\nMOMMY\nMOMOE\nMOMOS\nMOMOT\nMOMSY\nMOMUS\nMONAD\nMONAL\nMONAS\nMONCK\nMONDE\nMONDO\nMONDY\nMONED\nMONEL\nMONER\nMONES\nMONET\nMONEY\nMONGE\nMONGO\nMONGS\nMONIC\nMONIE\nMONIZ\nMONJE\nMONKS\nMONKY\nMONME\nMONNA\nMONNS\nMONOI\nMONOM\nMONOS\nMONRO\nMONTE\nMONTH\nMONTS\nMONTY\nMONTZ\nMONZO\nMOOBS\nMOOCH\nMOOCS\nMOODS\nMOODY\nMOOED\nMOOER\nMOOEY\nMOOFS\nMOOGS\nMOOKS\nMOOLA\nMOOLE\nMOOLI\nMOOLS\nMOONE\nMOONG\nMOONS\nMOONY\nMOORE\nMOORI\nMOORS\nMOORY\nMOOSE\nMOOSH\nMOOTA\nMOOTS\nMOOTW\nMOOTZ\nMOOVE\nMOPAN\nMOPED\nMOPER\nMOPES\nMOPEY\nMOPLA\nMOPOL\nMOPPY\nMOPSY\nMOPTI\nMOPUS\nMOQUI\nMORAE\nMORAI\nMORAL\nMORAN\nMORAS\nMORAT\nMORAY\nMORBS\nMOREA\nMORED\nMOREE\nMOREL\nMORES\nMORET\nMOREY\nMORFS\nMORGA\nMORIA\nMORIC\nMORID\nMORIL\nMORIN\nMORIO\nMORIS\nMORKS\nMORMO\nMORNA\nMORNE\nMORNS\nMORON\nMOROS\nMOROW\nMOROZ\nMORPH\nMORRA\nMORRO\nMORRS\nMORSE\nMORTA\nMORTO\nMORTS\nMORTY\nMORUA\nMORWE\nMOSBY\nMOSCA\nMOSCO\nMOSER\nMOSES\nMOSEY\nMOSHE\nMOSKS\nMOSSI\nMOSSO\nMOSSY\nMOSTE\nMOSTS\nMOSUL\nMOTAS\nMOTDS\nMOTED\nMOTEL\nMOTEN\nMOTES\nMOTET\nMOTHS\nMOTHY\nMOTIA\nMOTIF\nMOTON\nMOTOR\nMOTOS\nMOTSS\nMOTTA\nMOTTE\nMOTTI\nMOTTO\nMOTTS\nMOTTY\nMOTZA\nMOUAS\nMOUCH\nMOUDY\nMOUES\nMOUFS\nMOULD\nMOULT\nMOUND\nMOUNT\nMOURN\nMOUSA\nMOUSE\nMOUSY\nMOUTH\nMOUWS\nMOUZA\nMOVAL\nMOVED\nMOVER\nMOVES\nMOVIE\nMOVPE\nMOWED\nMOWEN\nMOWER\nMOWRY\nMOXAS\nMOXIE\nMOYAS\nMOYCE\nMOYER\nMOYES\nMOYLE\nMOZEE\nMOZOS\nMPEGS\nMPHYS\nMPIGI\nMPNST\nMPOTO\nMPPDA\nMPREG\nMPRET\nMRAAM\nMRAMS\nMRAPS\nMRBMS\nMRCAS\nMRCGP\nMRCVS\nMRHDS\nMRNAS\nMRROW\nMSASA\nMSCTA\nMSECS\nMSFCS\nMSIAN\nMSMES\nMSNBC\nMSRPC\nMSRPS\nMSTAR\nMSTED\nMSTID\nMSTIE\nMTBFS\nMTCNS\nMTDNA\nMTECS\nMTHFR\nMTOCS\nMTORR\nMTRNA\nMUBAH\nMUCAL\nMUCCI\nMUCES\nMUCHA\nMUCHO\nMUCIC\nMUCID\nMUCIN\nMUCKS\nMUCKY\nMUCOR\nMUCRO\nMUCTC\nMUCUS\nMUDAR\nMUDDY\nMUDER\nMUDGE\nMUDIK\nMUDIR\nMUDIS\nMUDON\nMUDRA\nMUFFS\nMUFFY\nMUFTI\nMUGAM\nMUGGY\nMUGIL\nMUGUP\nMUGUS\nMUHAS\nMUHLY\nMUIDS\nMUIFA\nMUING\nMUISE\nMUISM\nMUJIK\nMUJRA\nMUKES\nMUKIM\nMULAI\nMULAM\nMULAO\nMULAY\nMULCH\nMULCT\nMULED\nMULES\nMULEY\nMULGA\nMULID\nMULIS\nMULLA\nMULLO\nMULLS\nMULOS\nMULSE\nMULSH\nMULTI\nMUMAK\nMUMAS\nMUMAW\nMUMIE\nMUMMA\nMUMMS\nMUMMY\nMUMPS\nMUMPY\nMUMSY\nMUMUS\nMUNAR\nMUNCH\nMUNCY\nMUNDA\nMUNDO\nMUNDS\nMUNDT\nMUNDU\nMUNDY\nMUNGA\nMUNGE\nMUNGO\nMUNGS\nMUNIA\nMUNIR\nMUNIS\nMUNIZ\nMUNKS\nMUNOZ\nMUNRO\nMUNTS\nMUNTU\nMUNTZ\nMUONG\nMUONS\nMURAD\nMURAL\nMURAT\nMURAY\nMURCH\nMURDO\nMURED\nMURES\nMUREX\nMURFF\nMURGA\nMURGH\nMURHA\nMURID\nMURKS\nMURKY\nMUROS\nMURPH\nMURRE\nMURRI\nMURRS\nMURRY\nMURSE\nMURTH\nMURTI\nMURUS\nMURVA\nMURZA\nMUSAC\nMUSAF\nMUSAL\nMUSAR\nMUSCA\nMUSEA\nMUSED\nMUSER\nMUSES\nMUSET\nMUSHA\nMUSHY\nMUSIC\nMUSIT\nMUSKS\nMUSKY\nMUSOS\nMUSSO\nMUSSY\nMUSTA\nMUSTE\nMUSTH\nMUSTO\nMUSTS\nMUSTY\nMUTAH\nMUTAT\nMUTCH\nMUTED\nMUTER\nMUTES\nMUTEX\nMUTHA\nMUTHS\nMUTIC\nMUTIS\nMUTON\nMUTOS\nMUTSU\nMUTTS\nMUTUA\nMUXED\nMUXER\nMUXES\nMUZAK\nMUZZY\nMVULE\nMWANI\nMWDEU\nMWERA\nMWERU\nMYALL\nMYATT\nMYCIN\nMYDID\nMYDST\nMYEIK\nMYEKS\nMYERS\nMYHRE\nMYIDS\nMYINT\nMYLAR\nMYLER\nMYLES\nMYLNE\nMYNAH\nMYNAS\nMYNDE\nMYOGA\nMYOID\nMYOMA\nMYOPE\nMYOPS\nMYOPY\nMYRIE\nMYRNA\nMYRON\nMYRRH\nMYSIA\nMYSID\nMYSIS\nMYTHE\nMYTHI\nMYTHS\nMYTHY\nMYTON\nMYUNG\nMZEES\nNAABS\nNAACP\nNAAFI\nNAAIS\nNAANS\nNAATI\nNAATS\nNABAL\nNABAM\nNABAN\nNABBY\nNABER\nNABES\nNABID\nNABIS\nNABKS\nNABLA\nNABOB\nNABOR\nNABQS\nNACBA\nNACES\nNACHO\nNACKS\nNACRE\nNADAR\nNADER\nNADES\nNADIA\nNADIR\nNADPH\nNAEEM\nNAEPP\nNAEVE\nNAEVI\nNAFDI\nNAFEC\nNAFEN\nNAFKA\nNAFLD\nNAFTA\nNAGAI\nNAGAK\nNAGAS\nNAGGY\nNAGIS\nNAGLE\nNAGOR\nNAGRA\nNAHAR\nNAHUA\nNAHUM\nNAIAD\nNAIBS\nNAIDU\nNAIFS\nNAIJA\nNAIKS\nNAILS\nNAILY\nNAIMS\nNAING\nNAION\nNAIOP\nNAIRA\nNAIRN\nNAIRS\nNAIRU\nNAITO\nNAITP\nNAIVE\nNAJAF\nNAJAR\nNAJAS\nNAJIS\nNAKAI\nNAKBA\nNAKED\nNAKEN\nNAKER\nNAKES\nNAKEY\nNAKFA\nNAKHI\nNAKIE\nNAKIR\nNALAS\nNALCA\nNALED\nNALES\nNALGO\nNALLS\nNALLY\nNAMAZ\nNAMBA\nNAMBO\nNAMED\nNAMER\nNAMES\nNAMIB\nNAMMU\nNAMOA\nNAMPO\nNAMUL\nNAMUR\nNAMUS\nNANAS\nNANCE\nNANCY\nNANDA\nNANDI\nNANDO\nNANDS\nNANDU\nNANEA\nNANEZ\nNANGA\nNANGS\nNANMU\nNANNA\nNANNY\nNANTI\nNANTO\nNANTY\nNANTZ\nNAOCL\nNAOKO\nNAOMI\nNAPAS\nNAPED\nNAPES\nNAPOO\nNAPPA\nNAPPE\nNAPPI\nNAPPY\nNAPTS\nNAQBA\nNAQIB\nNAQVI\nNARAS\nNARCO\nNARCS\nNARDI\nNARDO\nNARDS\nNARES\nNAREW\nNAREZ\nNARGS\nNARIS\nNARKA\nNARKS\nNARKY\nNAROM\nNARON\nNARRA\nNARTH\nNARVA\nNARVI\nNASAL\nNASBA\nNASER\nNASES\nNASHI\nNASHO\nNASIR\nNASKH\nNASKY\nNASON\nNASOS\nNASPA\nNASRI\nNASRS\nNASSE\nNASTS\nNASTY\nNASUS\nNATAL\nNATCH\nNATES\nNATOS\nNATTO\nNATTY\nNATYA\nNAULT\nNAUNT\nNAURU\nNAUTA\nNAVAL\nNAVAR\nNAVED\nNAVEL\nNAVES\nNAVEW\nNAVIA\nNAVID\nNAVIE\nNAVIN\nNAVMC\nNAVVY\nNAWAB\nNAWAR\nNAWAZ\nNAWIN\nNAWLS\nNAWTH\nNAXAL\nNAXAR\nNAXOS\nNAYAK\nNAYED\nNAZAR\nNAZCA\nNAZES\nNAZIR\nNAZIS\nNBAER\nNBOME\nNCADD\nNCAUR\nNCCAM\nNCDNA\nNCIPC\nNCLDV\nNCOIC\nNCRNA\nNDALA\nNDALI\nNDARI\nNDNAS\nNDPER\nNDUJA\nNEACE\nNEACP\nNEAFL\nNEALE\nNEALS\nNEALY\nNEAMS\nNEAPS\nNEAPY\nNEARE\nNEARS\nNEARY\nNEASE\nNEATH\nNEATO\nNEATS\nNEAVE\nNEBBY\nNEBEK\nNEBEL\nNECCO\nNECHE\nNECKE\nNECKS\nNECRO\nNEDDS\nNEDDY\nNEEBS\nNEECE\nNEEDA\nNEEDE\nNEEDS\nNEEDY\nNEEKS\nNEELD\nNEELE\nNEELS\nNEELY\nNEEMB\nNEEMS\nNEENY\nNEEPS\nNEERA\nNEERS\nNEESE\nNEETS\nNEEZE\nNEFAS\nNEFFY\nNEGAR\nNEGEB\nNEGER\nNEGEV\nNEGRI\nNEGRO\nNEGUS\nNEHER\nNEHRA\nNEICE\nNEIDE\nNEIFE\nNEIFS\nNEIGH\nNEILL\nNEIRA\nNEIST\nNELDA\nNELGS\nNELIA\nNELLY\nNELMA\nNELON\nNEMAN\nNENAD\nNENES\nNENGE\nNENIA\nNEONS\nNEPAL\nNEPER\nNEPHI\nNEPHS\nNEPID\nNEPIT\nNERAL\nNERDO\nNERDS\nNERDY\nNERFS\nNERIA\nNERIO\nNEROL\nNEROS\nNERSC\nNERTS\nNERTZ\nNERVE\nNERVY\nNERYS\nNESBY\nNESES\nNESKI\nNESOI\nNESSA\nNESTS\nNETHS\nNETOP\nNETRA\nNETTS\nNETTY\nNEUKS\nNEUME\nNEUMS\nNEURA\nNEURO\nNEVAH\nNEVEL\nNEVEN\nNEVER\nNEVES\nNEVIL\nNEVIN\nNEVIS\nNEVUH\nNEVUS\nNEVVY\nNEWAH\nNEWAR\nNEWAY\nNEWBS\nNEWBY\nNEWBZ\nNEWCO\nNEWED\nNEWEL\nNEWER\nNEWES\nNEWFS\nNEWIE\nNEWLY\nNEWRY\nNEWSY\nNEWTH\nNEWTS\nNEXAL\nNEXIN\nNEXIT\nNEXUM\nNEXUS\nNEYRA\nNFBSK\nNFLER\nNGAIO\nNGAKA\nNGALA\nNGANS\nNGAPI\nNGEOS\nNGINA\nNGINX\nNGOMA\nNGONI\nNGRAM\nNGSOS\nNGSTS\nNGUNI\nNGUYS\nNGWEE\nNGWEI\nNHANS\nNHLBI\nNHLER\nNHSES\nNHTSA\nNIAAA\nNIAID\nNIALL\nNIANG\nNIAZI\nNICAD\nNICAM\nNICAS\nNICCA\nNICED\nNICEN\nNICER\nNICES\nNICEY\nNICHE\nNICKI\nNICKS\nNICKY\nNICOL\nNICOS\nNICUS\nNIDAL\nNIDAS\nNIDAY\nNIDCR\nNIDDA\nNIDES\nNIDGE\nNIDOR\nNIDRI\nNIDUS\nNIECE\nNIEFS\nNIEHS\nNIELD\nNIESE\nNIETO\nNIEVE\nNIFFS\nNIFFY\nNIFLE\nNIFOC\nNIFTS\nNIFTY\nNIGEL\nNIGER\nNIGES\nNIGGA\nNIGGY\nNIGHS\nNIGHT\nNIGHY\nNIGMS\nNIGON\nNIGRA\nNIGRE\nNIGUA\nNIHOA\nNIKAH\nNIKAU\nNIKES\nNIKIS\nNIKKA\nNIKKI\nNIKKO\nNIKOS\nNIKUD\nNILAS\nNILES\nNILLA\nNILLS\nNIMBI\nNIMBS\nNIMBY\nNIMES\nNIMHD\nNIMMO\nNIMOY\nNIMPH\nNIMUE\nNINAN\nNINAS\nNINDS\nNINER\nNINES\nNINGS\nNINJA\nNINNY\nNINON\nNINPO\nNINTH\nNINTY\nNINUS\nNIOBE\nNIOPO\nNIORT\nNIOSH\nNIPAS\nNIPPS\nNIPPY\nNIPSY\nNIQAB\nNIRLS\nNIRPS\nNISAB\nNISAN\nNISBA\nNISBE\nNISEI\nNISEY\nNISHI\nNISIN\nNISKU\nNISSE\nNISUS\nNITCH\nNITER\nNITES\nNITHE\nNITIC\nNITID\nNITON\nNITRE\nNITRO\nNITRY\nNITTA\nNITTI\nNITTO\nNITTY\nNIVAL\nNIVEN\nNIVER\nNIVKH\nNIXED\nNIXER\nNIXES\nNIXIE\nNIXON\nNIZAM\nNIZAR\nNJACS\nNJAHI\nNJIES\nNJOKU\nNKISI\nNKORE\nNKOTB\nNLETS\nNLRGS\nNMOLE\nNMOLS\nNNESS\nNNEWI\nNNSES\nNOACK\nNOAEL\nNOAHS\nNOBBE\nNOBBS\nNOBBY\nNOBEL\nNOBES\nNOBLE\nNOBLY\nNOBOA\nNOBOS\nNOCES\nNOCKS\nNODAL\nNODDY\nNODED\nNODES\nNODIS\nNODUS\nNOELE\nNOELL\nNOELS\nNOEMA\nNOGAI\nNOGAS\nNOGGY\nNOHOW\nNOIBN\nNOICE\nNOIDS\nNOIER\nNOILS\nNOILY\nNOINT\nNOIRS\nNOISE\nNOISY\nNOITS\nNOKIA\nNOLAN\nNOLDS\nNOLEN\nNOLES\nNOLID\nNOLIE\nNOLIN\nNOLLS\nNOLTE\nNOLTS\nNOMAD\nNOMAN\nNOMAP\nNOMAS\nNOMEN\nNOMES\nNOMIC\nNOMID\nNOMOI\nNOMOS\nNONAD\nNONCE\nNONCY\nNONDA\nNONDO\nNONES\nNONET\nNONGS\nNONIC\nNONIS\nNONNA\nNONNO\nNONNY\nNONOS\nNONYL\nNOOBS\nNOOBY\nNOOBZ\nNOOCH\nNOOGY\nNOOIT\nNOOKS\nNOOKY\nNOONE\nNOONS\nNOOOW\nNOORD\nNOORI\nNOOSE\nNOPAL\nNOPEC\nNOPED\nNOPES\nNOPEY\nNORAD\nNORAH\nNORAM\nNORBA\nNORBY\nNORCO\nNOREN\nNORGE\nNORIA\nNORIC\nNORIE\nNORIS\nNORKS\nNORMA\nNORML\nNORMS\nNORNS\nNORRA\nNORRY\nNORSE\nNORSK\nNORTE\nNORTH\nNOSAL\nNOSED\nNOSEK\nNOSEL\nNOSER\nNOSES\nNOSEY\nNOSHI\nNOSLE\nNOSQL\nNOSUH\nNOSYL\nNOTAH\nNOTAL\nNOTAM\nNOTAR\nNOTCH\nNOTEC\nNOTED\nNOTER\nNOTES\nNOTIF\nNOTTS\nNOTUM\nNOTUS\nNOUCH\nNOULD\nNOULE\nNOUNS\nNOUNY\nNOURI\nNOUSE\nNOUST\nNOUTS\nNOVAE\nNOVAK\nNOVAS\nNOVEL\nNOVEX\nNOVIS\nNOVOA\nNOVOS\nNOVUM\nNOVYS\nNOWAK\nNOWAY\nNOWCH\nNOWDS\nNOWED\nNOWEL\nNOWHY\nNOWTS\nNOXAL\nNOYAN\nNOYAU\nNOYED\nNOYER\nNOYLS\nNOYON\nNOYPI\nNOYSE\nNOZLE\nNPAPI\nNPSCS\nNRARP\nNRDNA\nNREMT\nNRNAS\nNROFF\nNROTC\nNSAID\nNSDAP\nNSERC\nNSIMA\nNSPCC\nNSRNA\nNSWBC\nNTBCW\nNTIDS\nNTLDR\nNTUZU\nNUBBY\nNUBIA\nNUCCI\nNUCHA\nNUCIN\nNUDDY\nNUDER\nNUDES\nNUDEY\nNUDGE\nNUDGY\nNUDIE\nNUDLE\nNUDZH\nNUEIR\nNUERS\nNUEVO\nNUFFY\nNUHOU\nNUKED\nNUKER\nNUKES\nNUKUS\nNULAB\nNULLA\nNULLS\nNULPH\nNUMBS\nNUMBY\nNUMEN\nNUMIC\nNUMMI\nNUMMY\nNUMPS\nNUNEZ\nNUNGA\nNUNKI\nNUNLY\nNUNNS\nNUNTY\nNUNUS\nNUNYA\nNUORO\nNUPUR\nNUQTA\nNUQUE\nNURBS\nNURDS\nNURDY\nNURLS\nNURSE\nNURST\nNURSY\nNURUK\nNUSAS\nNUTIL\nNUTLY\nNUTSO\nNUTSY\nNUTTS\nNUTTY\nNUTZO\nNUWHO\nNUWSS\nNUZUM\nNUZZI\nNUZZO\nNVCJD\nNVLDS\nNVOCC\nNVRAM\nNWEMS\nNWFZS\nNWHRC\nNWOSU\nNWTCA\nNWTSC\nNYALA\nNYAMI\nNYAYA\nNYCER\nNYCES\nNYCTA\nNYDRI\nNYEMS\nNYEPI\nNYIHA\nNYJER\nNYLAH\nNYLON\nNYMPH\nNYSSA\nNYULA\nNZADI\nNZEMA\nNZERS\nNZIMA\nNZPIF\nOADBY\nOADMS\nOAERS\nOAKED\nOAKEN\nOAKER\nOAKES\nOAKUM\nOARED\nOASAL\nOASES\nOASIS\nOASTS\nOATEN\nOATER\nOATES\nOATHS\nOATUS\nOAVES\nOBAKE\nOBAMA\nOBANG\nOBEAH\nOBEDT\nOBELI\nOBESE\nOBEYS\nOBGYN\nOBHWF\nOBIES\nOBION\nOBITS\nOBLEY\nOBOES\nOBOLE\nOBOLI\nOBOLO\nOBOLS\nOBROK\nOCCUR\nOCEAN\nOCHER\nOCHES\nOCHRA\nOCHRE\nOCHRY\nOCKER\nOCOID\nOCRAS\nOCREA\nOCRED\nOCRES\nOCTAD\nOCTAL\nOCTAN\nOCTAS\nOCTET\nOCTGS\nOCTIC\nOCTLI\nOCTYL\nOCULI\nODALS\nODDEN\nODDER\nODDLY\nODELL\nODEMA\nODEON\nODESA\nODEUM\nODHNI\nODIAS\nODIHR\nODIID\nODISM\nODIST\nODIUM\nODIZE\nODMYL\nODORS\nODOUR\nODTAA\nODYLE\nOECUS\nOENIN\nOENUS\nOFALA\nOFAYS\nOFERS\nOFFAL\nOFFED\nOFFEN\nOFFER\nOFFIE\nOFFLY\nOFFRE\nOFGEM\nOFLAG\nOFLOT\nOFSPS\nOFTEN\nOFTER\nOFUDA\nOFURO\nOGAMS\nOGATA\nOGAWA\nOGDEN\nOGEED\nOGEES\nOGEMA\nOGGIN\nOGGLE\nOGHAM\nOGHUZ\nOGIVE\nOGLED\nOGLER\nOGLES\nOGLIO\nOGMIC\nOGONI\nOGRES\nOHANA\nOHELO\nOHIAH\nOHIAS\nOHING\nOHKOS\nOHMIC\nOHRID\nOIDIA\nOIKOI\nOIKOS\nOILED\nOILER\nOINKS\nOINTS\nOIRAN\nOISHI\nOITNB\nOJEKS\nOJHAS\nOJIME\nOKADA\nOKAPI\nOKARA\nOKAWA\nOKAYS\nOKEHS\nOKERS\nOKIES\nOKINA\nOKING\nOKISH\nOKIYA\nOKOLE\nOKRAS\nOKRUG\nOKTAS\nOKUNG\nOKWAS\nOLAND\nOLDEN\nOLDER\nOLDIE\nOLDLY\nOLEAS\nOLEDS\nOLEHS\nOLEIC\nOLEIN\nOLEMA\nOLENE\nOLENT\nOLEON\nOLEOS\nOLESH\nOLEUM\nOLEYL\nOLIGO\nOLIOS\nOLIVA\nOLIVE\nOLLAM\nOLLAS\nOLLAV\nOLLIE\nOLMEC\nOLNEY\nOLOGY\nOLOID\nOLONA\nOLPES\nOLSON\nOLTPS\nOLWEN\nOLWYN\nOMAGH\nOMAHA\nOMAKE\nOMANI\nOMARI\nOMASA\nOMATA\nOMBER\nOMBRE\nOMBUD\nOMBUS\nOMDBS\nOMEES\nOMEGA\nOMENA\nOMENS\nOMERS\nOMIAI\nOMICS\nOMIKO\nOMINE\nOMITS\nOMKAR\nOMLAH\nOMOTO\nOMRLP\nOMULS\nOMURA\nONAWA\nONAYE\nONCED\nONCER\nONCET\nONCOM\nONDED\nONDES\nONDOL\nONEGA\nONEGO\nONEGS\nONELY\nONERS\nONERY\nONEST\nONETH\nONGEE\nONIDA\nONING\nONION\nONIUM\nONKUS\nONLAP\nONLAY\nONLEY\nONLIE\nONLYS\nONSEN\nONSET\nONTIC\nONTOP\nONTOS\nONYON\nONZAS\nOOAKS\nOOBES\nOOBIT\nOOCSS\nOOFDA\nOOFTA\nOOGLE\nOOHED\nOOIDS\nOOJAH\nOOMPF\nOOMPH\nOONTS\nOONTZ\nOOOHS\nOOOOH\nOOPAK\nOOPED\nOOPLS\nOOPSY\nOORAH\nOORIE\nOORYA\nOOSED\nOOSER\nOOSES\nOOTID\nOOWOP\nOOXML\nOOYAH\nOOZED\nOOZEL\nOOZES\nOOZIE\nOPAHS\nOPAKE\nOPALS\nOPAMP\nOPARA\nOPCON\nOPENS\nOPEPE\nOPERA\nOPERE\nOPERS\nOPHAN\nOPHIR\nOPIAS\nOPINE\nOPING\nOPIOD\nOPIUM\nOPLAX\nOPOLE\nOPPED\nOPPOS\nOPRAF\nOPRAH\nOPROV\nOPSEC\nOPSIN\nOPSON\nOPTED\nOPTER\nOPTIC\nORACH\nORACY\nORALE\nORALS\nORANG\nORANS\nORANT\nORATE\nORBAN\nORBAT\nORBED\nORBIC\nORBIT\nORCAS\nORCIN\nORCOS\nORCUS\nORDER\nORDES\nORDIE\nORDOS\nORDRE\nOREAD\nOREOS\nORFED\nORFEN\nORFES\nORFUL\nORGAL\nORGAN\nORGED\nORGEL\nORGIC\nORGIE\nORGUE\nORGUL\nORIBI\nORIEL\nORIGO\nORINS\nORIOL\nORION\nORISA\nORIXA\nORIYA\nORLAY\nORLED\nORLES\nORLON\nORLOP\nORLOS\nORMER\nORMUS\nORNED\nORNEE\nORNGE\nORNIS\nOROMO\nOROYA\nORPED\nORPIN\nORREN\nORRIN\nORRIS\nORSON\nORTED\nORTET\nORTHO\nORTIZ\nORTOL\nORTON\nORUJO\nORUTU\nORVAL\nORVET\nORVIN\nORYOL\nOSAGE\nOSAKA\nOSAMA\nOSAMU\nOSARS\nOSAWA\nOSCAN\nOSCAR\nOSETE\nOSETI\nOSHAC\nOSIER\nOSITE\nOSLER\nOSMAN\nOSMIC\nOSMOL\nOSONE\nOSSEO\nOSSES\nOSSET\nOSSIA\nOSSIE\nOSSOM\nOSTEA\nOSTEO\nOSTIA\nOSTIC\nOSTOJ\nOSTRO\nOTAGO\nOTAKU\nOTANI\nOTARY\nOTECS\nOTERO\nOTHER\nOTOMI\nOTOPY\nOTTAR\nOTTER\nOTTIE\nOTTIS\nOTTOS\nOTWAY\nOUCHE\nOUCHI\nOUCHY\nOUENS\nOUGHT\nOUGLY\nOUIJA\nOUKIE\nOULES\nOUNCE\nOUNDY\nOUNSI\nOUPHE\nOUPHS\nOURAY\nOUREA\nOURIE\nOUSED\nOUSEL\nOUSES\nOUSIA\nOUSIE\nOUSTS\nOUTBY\nOUTDO\nOUTED\nOUTEN\nOUTER\nOUTGO\nOUTIE\nOUTRE\nOUTRO\nOUTTA\nOUVRE\nOUZEL\nOUZOS\nOVALS\nOVANT\nOVARY\nOVATE\nOVENS\nOVERO\nOVERS\nOVERT\nOVEST\nOVFST\nOVINE\nOVISM\nOVIST\nOVOID\nOVOLO\nOVOOS\nOVULA\nOVULE\nOVULO\nOWARE\nOWARI\nOWCHE\nOWEGO\nOWENS\nOWERS\nOWEST\nOWETH\nOWIES\nOWING\nOWLED\nOWLER\nOWLET\nOWNED\nOWNER\nOWORD\nOWRES\nOWSEN\nOWTTE\nOWWIE\nOWZAT\nOXANE\nOXBOW\nOXEAE\nOXEAS\nOXEIA\nOXENE\nOXENS\nOXERS\nOXEYE\nOXFAM\nOXFLY\nOXHEY\nOXIAE\nOXIDE\nOXIDS\nOXIME\nOXIMS\nOXINE\nOXISH\nOXLEY\nOXLIP\nOXOLE\nOXONS\nOXTED\nOXTER\nOXYDE\nOXYDS\nOXYLS\nOYAMA\nOYENS\nOYLET\nOYRUR\nOZALJ\nOZARK\nOZAWA\nOZEKI\nOZIAN\nOZIER\nOZITE\nOZONA\nOZONE\nOZREN\nOZUMO\nOZZIE\nPAAMA\nPAANS\nPAARL\nPABON\nPABST\nPACAS\nPACAY\nPACED\nPACER\nPACES\nPACEY\nPACHA\nPACHY\nPACIS\nPACKS\nPACKY\nPACOS\nPACTS\nPACUS\nPADAM\nPADAR\nPADAS\nPADDY\nPADEK\nPADEL\nPADEN\nPADGE\nPADIN\nPADLE\nPADMA\nPADOW\nPADRE\nPADRO\nPADUA\nPAEAN\nPAEDO\nPAEDS\nPAEKS\nPAEON\nPAFFS\nPAGAN\nPAGDI\nPAGED\nPAGEL\nPAGER\nPAGES\nPAGET\nPAGNE\nPAGOD\nPAGRI\nPAGUS\nPAHAN\nPAHIS\nPAHLS\nPAHUS\nPAICH\nPAIDE\nPAIGE\nPAIKS\nPAILA\nPAILS\nPAIME\nPAINE\nPAINS\nPAINT\nPAINY\nPAIPO\nPAIRE\nPAIRS\nPAISA\nPAISE\nPAITS\nPAIZA\nPAJAK\nPAKER\nPAKIS\nPAKOL\nPAKUL\nPALAE\nPALAK\nPALAS\nPALAU\nPALEA\nPALED\nPALEN\nPALEO\nPALER\nPALES\nPALET\nPALEY\nPALIN\nPALKA\nPALKI\nPALKO\nPALLA\nPALLS\nPALLU\nPALLY\nPALMA\nPALME\nPALMS\nPALMY\nPALOV\nPALPI\nPALPS\nPALSA\nPALSY\nPALUS\nPAMIR\nPAMMY\nPAMPA\nPANAY\nPANCE\nPANCH\nPANCY\nPANDA\nPANDO\nPANDU\nPANDY\nPANED\nPANEK\nPANEL\nPANES\nPANGA\nPANGS\nPANGU\nPANIA\nPANIC\nPANIM\nPANIR\nPANKO\nPANKS\nPANNE\nPANNI\nPANNU\nPANSY\nPANTH\nPANTO\nPANTS\nPANTY\nPANUS\nPANYM\nPANZA\nPAOLA\nPAOLI\nPAOLO\nPAONE\nPAPAD\nPAPAL\nPAPAS\nPAPAU\nPAPAW\nPAPDI\nPAPER\nPAPES\nPAPIK\nPAPIN\nPAPIS\nPAPKE\nPAPPA\nPAPPI\nPAPPY\nPAPRI\nPAPRS\nPAPUA\nPARAM\nPARAS\nPARCH\nPARCS\nPARDI\nPARDO\nPARDS\nPARDY\nPARED\nPAREE\nPAREN\nPAREO\nPARER\nPARES\nPAREV\nPARGA\nPARGE\nPARID\nPARIS\nPARKA\nPARKE\nPARKI\nPARKS\nPARKY\nPARLA\nPARLE\nPARMA\nPARMO\nPARNU\nPAROL\nPAROS\nPARPI\nPARPS\nPARRA\nPARRO\nPARRS\nPARRY\nPARSA\nPARSE\nPARSI\nPARTE\nPARTI\nPARTS\nPARTY\nPARVE\nPARVO\nPASAN\nPASCH\nPASCO\nPASEK\nPASEO\nPASGT\nPASHA\nPASKA\nPASMA\nPASOK\nPASOS\nPASPY\nPASSE\nPASTA\nPASTE\nPASTS\nPASTY\nPATAS\nPATCH\nPATED\nPATEL\nPATEN\nPATER\nPATES\nPATHE\nPATHS\nPATHY\nPATIA\nPATIL\nPATIN\nPATIO\nPATIS\nPATKA\nPATLY\nPATNA\nPATON\nPATSY\nPATTE\nPATTI\nPATTY\nPATUS\nPAUAS\nPAUCE\nPAUGH\nPAULA\nPAULI\nPAULK\nPAULL\nPAULS\nPAULY\nPAUMS\nPAUNE\nPAUNI\nPAUSA\nPAUSE\nPAUTZ\nPAUXI\nPAVAN\nPAVED\nPAVEE\nPAVEK\nPAVEL\nPAVEN\nPAVER\nPAVES\nPAVEY\nPAVIA\nPAVID\nPAVIS\nPAVON\nPAWAR\nPAWAS\nPAWAW\nPAWED\nPAWER\nPAWKS\nPAWKY\nPAWLE\nPAWLS\nPAWNE\nPAWNS\nPAWTY\nPAXES\nPAXIL\nPAXIS\nPAYAM\nPAYAN\nPAYAO\nPAYED\nPAYEE\nPAYEN\nPAYER\nPAYES\nPAYGO\nPAYNE\nPAYNI\nPAYOR\nPAYOS\nPAYOT\nPAYSE\nPBAOE\nPBARS\nPBGVS\nPBMER\nPCASP\nPCAST\nPCATS\nPCDNA\nPCING\nPCISM\nPCRED\nPCSOS\nPDFED\nPDGFS\nPDLCS\nPDNOS\nPDOCS\nPEABO\nPEACE\nPEACH\nPEAGE\nPEAKE\nPEAKS\nPEAKY\nPEALS\nPEANO\nPEANS\nPEARE\nPEARL\nPEARS\nPEART\nPEARY\nPEASE\nPEASY\nPEATE\nPEATS\nPEATY\nPEAVY\nPEAYS\nPEBAS\nPEBLO\nPEBLY\nPECAN\nPECCO\nPECHA\nPECHS\nPECHT\nPECKE\nPECKS\nPECKY\nPECOR\nPECOS\nPECUL\nPEDAL\nPEDAS\nPEDEN\nPEDES\nPEDHA\nPEDIA\nPEDIS\nPEDON\nPEDOS\nPEDOT\nPEDRO\nPEDUM\nPEECE\nPEEDS\nPEEKS\nPEELE\nPEELS\nPEENS\nPEENT\nPEEPE\nPEEPS\nPEEPY\nPEERS\nPEERT\nPEERY\nPEETE\nPEEVE\nPEEVY\nPEFCS\nPEGAH\nPEGAN\nPEGGS\nPEGGY\nPEGOL\nPEGOS\nPEGYL\nPEICE\nPEILS\nPEINE\nPEISE\nPEITC\nPEIZE\nPEKAN\nPEKAR\nPEKES\nPEKID\nPEKIE\nPEKIN\nPEKOE\nPELAU\nPELCS\nPELEA\nPELED\nPELEG\nPELLA\nPELLS\nPELLY\nPELMA\nPELOG\nPELTA\nPELTO\nPELTS\nPELTZ\nPELVE\nPEMON\nPENAL\nPENAM\nPENCE\nPENDS\nPENED\nPENEM\nPENES\nPENGE\nPENGO\nPENHA\nPENII\nPENIN\nPENIS\nPENIX\nPENKS\nPENNA\nPENNE\nPENNY\nPENRY\nPENTA\nPENTS\nPENTZ\nPENUP\nPENZA\nPEONS\nPEONY\nPEOPS\nPEPAW\nPEPEL\nPEPER\nPEPIN\nPEPLA\nPEPLE\nPEPLI\nPEPOS\nPEPPY\nPEPSI\nPEPTO\nPEPYS\nPEQUI\nPERAK\nPERAS\nPERCE\nPERCH\nPERCY\nPERDU\nPERDY\nPEREA\nPEREQ\nPERES\nPERET\nPEREZ\nPERFS\nPERIL\nPERIN\nPERIS\nPERKO\nPERKS\nPERKY\nPERMS\nPERNA\nPERNE\nPERNS\nPERNT\nPEROS\nPEROT\nPEROX\nPERPS\nPERQS\nPERRI\nPERRL\nPERRO\nPERRY\nPERSE\nPERTH\nPERTS\nPERUN\nPERVE\nPERVO\nPERVS\nPERVY\nPESCE\nPESEK\nPESEN\nPESES\nPESKY\nPESOS\nPESTA\nPESTO\nPESTS\nPESTY\nPETAL\nPETAR\nPETER\nPETEY\nPETHA\nPETIE\nPETIT\nPETRA\nPETRE\nPETRO\nPETRY\nPETTI\nPETTO\nPETTS\nPETTY\nPETUN\nPEUCE\nPEUGH\nPEUMO\nPEVEY\nPEWED\nPEWEE\nPEWET\nPEWIT\nPEYER\nPFAAS\nPFAFF\nPFALZ\nPFARR\nPFCAS\nPFIGS\nPFLUM\nPFOHL\nPFOSA\nPGACC\nPGCES\nPGDMS\nPGLAF\nPGRNA\nPGSEM\nPHAAL\nPHACO\nPHAGE\nPHAIR\nPHALL\nPHALP\nPHALS\nPHAMS\nPHANE\nPHANG\nPHANS\nPHARE\nPHARM\nPHARO\nPHARR\nPHASE\nPHASM\nPHATS\nPHEBE\nPHEEP\nPHEER\nPHELP\nPHEMY\nPHENE\nPHEON\nPHEVS\nPHFFT\nPHIAL\nPHIFE\nPHILO\nPHILP\nPHIPP\nPHIPS\nPHISH\nPHITS\nPHLOG\nPHLOX\nPHOBE\nPHOCA\nPHONE\nPHONG\nPHONO\nPHONS\nPHONY\nPHOTO\nPHOTS\nPHPER\nPHPHT\nPHRAE\nPHRAG\nPHREN\nPHUBS\nPHUCK\nPHUNG\nPHUTS\nPHYLA\nPHYLE\nPHYMA\nPHYSA\nPIANO\nPIATT\nPIAUI\nPIAYE\nPIBAL\nPICAS\nPICCS\nPICCY\nPICES\nPICHA\nPICHE\nPICID\nPICKS\nPICKT\nPICKY\nPICON\nPICOS\nPICOT\nPICOU\nPICOW\nPICRA\nPICTS\nPICUL\nPICUS\nPIDES\nPIDGE\nPIECE\nPIEHL\nPIELS\nPIEMS\nPIEND\nPIERS\nPIETA\nPIETS\nPIETY\nPIETZ\nPIEZO\nPIFER\nPIFFS\nPIGAS\nPIGGS\nPIGGY\nPIGHT\nPIGMS\nPIGMY\nPIHLS\nPIIGG\nPIIGS\nPIING\nPIJIN\nPIKAS\nPIKED\nPIKER\nPIKES\nPIKEY\nPIKIE\nPIKUL\nPILAE\nPILAF\nPILAR\nPILAT\nPILAU\nPILAV\nPILAW\nPILCH\nPILEA\nPILED\nPILEI\nPILER\nPILES\nPILID\nPILIN\nPILIS\nPILLA\nPILLS\nPILLY\nPILON\nPILOT\nPILOW\nPILUM\nPILUS\nPIMAN\nPIMPS\nPIMPY\nPINAL\nPINAS\nPINAX\nPINAY\nPINCH\nPINDA\nPINED\nPINER\nPINES\nPINEY\nPINGO\nPINGS\nPINGY\nPINHO\nPINID\nPININ\nPINKO\nPINKS\nPINKY\nPINNA\nPINNY\nPINON\nPINOT\nPINOY\nPINSK\nPINTA\nPINTO\nPINTS\nPINUP\nPINUS\nPIONS\nPIONY\nPIOTS\nPIOUS\nPIPAL\nPIPAS\nPIPED\nPIPEL\nPIPER\nPIPES\nPIPET\nPIPID\nPIPIL\nPIPIS\nPIPIT\nPIPPA\nPIPPY\nPIPRA\nPIPUL\nPIQUA\nPIQUE\nPIRAI\nPIREP\nPIRGS\nPIRIE\nPIRKS\nPIRLS\nPIRNA\nPIRNS\nPIROG\nPIROS\nPIRRY\nPISAN\nPISCO\nPISES\nPISIQ\nPISKY\nPISSY\nPISTE\nPITAS\nPITBS\nPITCH\nPITHS\nPITHY\nPITIE\nPITIS\nPITON\nPITOT\nPITRE\nPITTA\nPITTS\nPITTY\nPIVKA\nPIVOT\nPIWIS\nPIXAR\nPIXEL\nPIXES\nPIXEY\nPIXIE\nPIXIU\nPIYAG\nPIZED\nPIZER\nPIZES\nPIZZA\nPIZZE\nPIZZI\nPIZZO\nPKERS\nPLAAS\nPLACE\nPLACK\nPLAGA\nPLAGE\nPLAID\nPLAIN\nPLAIR\nPLAIT\nPLANA\nPLANE\nPLANK\nPLANO\nPLANS\nPLANT\nPLAPS\nPLARN\nPLASH\nPLASM\nPLATA\nPLATE\nPLATH\nPLATO\nPLATS\nPLATT\nPLATY\nPLATZ\nPLAUD\nPLAWS\nPLAYA\nPLAYE\nPLAYS\nPLAZA\nPLCCS\nPLDNA\nPLEAD\nPLEAS\nPLEAT\nPLEBE\nPLEBS\nPLECK\nPLECO\nPLECS\nPLEDG\nPLEDS\nPLEID\nPLEMS\nPLENA\nPLENE\nPLEON\nPLESH\nPLEWA\nPLEWD\nPLEWS\nPLEXI\nPLEYT\nPLICA\nPLIED\nPLIER\nPLIES\nPLIGS\nPLIMS\nPLING\nPLINK\nPLINY\nPLIPS\nPLISH\nPLITT\nPLMNS\nPLOCE\nPLODS\nPLONK\nPLOOF\nPLOOP\nPLOOT\nPLOPS\nPLOTS\nPLOTZ\nPLOVS\nPLOWS\nPLOYE\nPLOYS\nPLUCK\nPLUFF\nPLUGS\nPLUMA\nPLUMB\nPLUME\nPLUMP\nPLUMS\nPLUMY\nPLUNK\nPLUOT\nPLUSH\nPLUTA\nPLUTE\nPLUTO\nPLYER\nPLYGS\nPLYOS\nPLZEN\nPMIDS\nPMING\nPMOLE\nPMOLS\nPMOYS\nPMSED\nPMSES\nPMTCT\nPOACH\nPOAGS\nPOAKA\nPOAST\nPOBBY\nPOBER\nPOCAN\nPOCHE\nPOCKS\nPOCKY\nPOCOS\nPODDY\nPODES\nPODGE\nPODGY\nPODIA\nPOEAA\nPOEAN\nPOEME\nPOEMS\nPOESY\nPOETE\nPOETS\nPOFFS\nPOGEY\nPOGGE\nPOGGI\nPOGGY\nPOGIE\nPOGOS\nPOGUE\nPOIDS\nPOILU\nPOIND\nPOINT\nPOISE\nPOIZE\nPOJOS\nPOKAL\nPOKED\nPOKER\nPOKES\nPOKEY\nPOKIE\nPOKOT\nPOLAN\nPOLAO\nPOLAR\nPOLED\nPOLER\nPOLES\nPOLEY\nPOLIO\nPOLIS\nPOLJE\nPOLKA\nPOLKI\nPOLKS\nPOLLS\nPOLLY\nPOLOS\nPOLSA\nPOLTS\nPOLUS\nPOLYP\nPOLYS\nPOMAS\nPOMBE\nPOMBO\nPOMES\nPOMEY\nPOMME\nPOMMY\nPOMPA\nPOMPS\nPONCA\nPONCE\nPONCY\nPONDS\nPONDY\nPONES\nPONEY\nPONGA\nPONGO\nPONGS\nPONGY\nPONIR\nPONOR\nPONTY\nPONZI\nPONZO\nPONZU\nPOOCH\nPOODS\nPOOED\nPOOER\nPOOEY\nPOOFS\nPOOFY\nPOOGH\nPOOHS\nPOOID\nPOOJA\nPOOKA\nPOOLE\nPOOLS\nPOOMA\nPOONA\nPOONS\nPOOPS\nPOOPY\nPOORE\nPOORI\nPOOTS\nPOOTY\nPOOVE\nPOOVY\nPOPAL\nPOPAS\nPOPED\nPOPES\nPOPOS\nPOPOV\nPOPPA\nPOPPE\nPOPPY\nPOPSY\nPOPUP\nPORAL\nPORCH\nPORCO\nPOREC\nPORED\nPORER\nPORES\nPORGA\nPORGY\nPORIA\nPORIN\nPORKI\nPORKS\nPORKY\nPORNO\nPORNS\nPORNY\nPORTA\nPORTE\nPORTO\nPORTS\nPORTZ\nPOSCA\nPOSED\nPOSEK\nPOSER\nPOSES\nPOSET\nPOSEY\nPOSHO\nPOSIO\nPOSIT\nPOSIX\nPOSSA\nPOSSE\nPOSTH\nPOSTS\nPOTCH\nPOTDS\nPOTED\nPOTES\nPOTHS\nPOTIN\nPOTOO\nPOTSY\nPOTTO\nPOTTS\nPOTTY\nPOTUS\nPOUCH\nPOUDS\nPOUFS\nPOUFY\nPOUGH\nPOUIS\nPOULE\nPOULP\nPOULT\nPOUND\nPOURS\nPOUTS\nPOUTY\nPOVEY\nPOVVO\nPOWAH\nPOWAN\nPOWEN\nPOWER\nPOWES\nPOWND\nPOWRE\nPOWYS\nPOXED\nPOXES\nPOYER\nPOYNT\nPOYSE\nPOZED\nPOZES\nPOZOS\nPOZZI\nPOZZY\nPPBAR\nPPPOA\nPPPOE\nPRAAM\nPRACS\nPRADS\nPRAHL\nPRAHM\nPRAHU\nPRAIA\nPRAKS\nPRALL\nPRAME\nPRAMS\nPRANA\nPRANG\nPRANK\nPRAOS\nPRAPS\nPRASE\nPRATA\nPRATE\nPRATO\nPRATS\nPRATT\nPRATY\nPRAUS\nPRAWN\nPRAYE\nPRAYS\nPRDNA\nPREDE\nPREDS\nPREDY\nPREEM\nPREEN\nPREFS\nPREGO\nPREGS\nPREKE\nPRELL\nPREMO\nPREMS\nPREMY\nPREON\nPREOP\nPREPS\nPRESA\nPRESH\nPRESO\nPRESS\nPREST\nPRETA\nPRETE\nPRETT\nPREVE\nPREVO\nPREXY\nPREYS\nPREZA\nPRIAL\nPRIAM\nPRIAN\nPRIAR\nPRICE\nPRICK\nPRICY\nPRIDE\nPRIED\nPRIEF\nPRIER\nPRIES\nPRIGS\nPRILE\nPRILL\nPRIMA\nPRIME\nPRIMM\nPRIMO\nPRIMP\nPRIMS\nPRIMY\nPRINE\nPRINK\nPRINT\nPRION\nPRIOR\nPRISE\nPRISM\nPRISS\nPRITT\nPRIVS\nPRIVY\nPRIYA\nPRIZE\nPRNDL\nPRNGS\nPROAS\nPROBE\nPROBS\nPROCK\nPROCS\nPRODD\nPRODS\nPROEM\nPROFS\nPROGG\nPROGS\nPROIN\nPROKE\nPROLE\nPROLL\nPROLY\nPROME\nPROMO\nPROMS\nPRONE\nPRONG\nPRONK\nPRONS\nPROOF\nPROOT\nPROPP\nPROPS\nPRORE\nPROSE\nPROSO\nPROSS\nPROST\nPROSY\nPROTO\nPROUD\nPROUL\nPROUT\nPROVE\nPROVO\nPROWD\nPROWL\nPROWS\nPROXY\nPRRSV\nPRUCE\nPRUCK\nPRUDE\nPRUNE\nPRUNO\nPRUNT\nPRUNY\nPRUSS\nPRUTA\nPRYAN\nPRYCE\nPRYDE\nPRYER\nPRYOR\nPSAKI\nPSALM\nPSAPS\nPSARA\nPSDPS\nPSEUD\nPSHAW\nPSHHT\nPSION\nPSKOV\nPSNRS\nPSOAE\nPSOAI\nPSOAS\nPSORA\nPSPOS\nPSRNA\nPSSHT\nPSSST\nPSTNS\nPSYCH\nPSYOP\nPTAKS\nPTBNL\nPTDFS\nPTDNA\nPTRNA\nPTSAS\nPUACS\nPUBBY\nPUBCO\nPUBED\nPUBES\nPUBEY\nPUBIC\nPUBIS\nPUCCA\nPUCEL\nPUCES\nPUCKA\nPUCKS\nPUCKY\nPUDDS\nPUDGE\nPUDGY\nPUDIC\nPUDOR\nPUDUS\nPUETS\nPUETT\nPUETZ\nPUFAS\nPUFFA\nPUFFS\nPUFFY\nPUGAS\nPUGET\nPUGGY\nPUGHE\nPUGIL\nPUGIO\nPUGNE\nPUHLS\nPUITS\nPUJAH\nPUJAS\nPUKAO\nPUKAS\nPUKED\nPUKER\nPUKES\nPUKEY\nPUKIS\nPUKKA\nPUKUS\nPULAO\nPULAS\nPULED\nPULEO\nPULER\nPULES\nPULIK\nPULIS\nPULKA\nPULKS\nPULLA\nPULLI\nPULLS\nPULLY\nPULPS\nPULPY\nPULSE\nPUMAS\nPUMPS\nPUMSE\nPUNAS\nPUNCH\nPUNGA\nPUNGI\nPUNGS\nPUNGY\nPUNIC\nPUNIM\nPUNJI\nPUNKA\nPUNKS\nPUNKY\nPUNNY\nPUNTA\nPUNTE\nPUNTI\nPUNTO\nPUNTS\nPUNTY\nPUOYS\nPUPAE\nPUPAL\nPUPAS\nPUPES\nPUPIL\nPUPOS\nPUPPP\nPUPPY\nPURAM\nPURDA\nPURDY\nPURED\nPUREE\nPURER\nPURES\nPUREX\nPUREY\nPURGA\nPURGE\nPURIF\nPURIM\nPURIS\nPURLE\nPURLS\nPURLY\nPUROK\nPUROS\nPURRE\nPURRS\nPURRY\nPURSE\nPURSY\nPURTY\nPURUM\nPUSAN\nPUSCA\nPUSEY\nPUSHT\nPUSHY\nPUSIL\nPUSLE\nPUSSY\nPUSTA\nPUTER\nPUTID\nPUTIN\nPUTOO\nPUTRE\nPUTRY\nPUTTI\nPUTTO\nPUTTS\nPUTTY\nPUYAS\nPUZIO\nPWISE\nPWNED\nPYATT\nPYCNA\nPYELO\nPYETS\nPYGAL\nPYGMY\nPYKAR\nPYKES\nPYKNA\nPYLAE\nPYLES\nPYLON\nPYLOS\nPYNED\nPYNES\nPYNNE\nPYOID\nPYORE\nPYOTR\nPYOTS\nPYOWS\nPYPER\nPYRAL\nPYRAM\nPYRAN\nPYREE\nPYRES\nPYREX\nPYRIN\nPYRON\nPYROS\nPYSMA\nPYXED\nPYXES\nPYXIE\nPYXIS\nPZAZZ\nPZEVS\nQAAFS\nQABAH\nQADAR\nQADIR\nQADIS\nQADRI\nQAFAR\nQAFIZ\nQAGAN\nQAHAL\nQAIDS\nQAJAQ\nQALAM\nQALYS\nQAMAS\nQANAT\nQANGO\nQANON\nQANUN\nQAPIK\nQAPPS\nQARGI\nQARIN\nQARIS\nQASAB\nQASRS\nQATAR\nQAWAL\nQAZAQ\nQAZAX\nQAZIS\nQBICS\nQBISM\nQBITS\nQCEPO\nQDIIS\nQEEMA\nQEPIK\nQEROS\nQFETS\nQFIIS\nQIANA\nQIANG\nQIANS\nQIAOS\nQIBLA\nQIBLI\nQILIN\nQIMEN\nQINAH\nQINGS\nQINOT\nQINPU\nQIPAO\nQIRAN\nQIRAT\nQIRSH\nQISAS\nQIYAM\nQIYAS\nQLEDS\nQOBAR\nQOBYZ\nQOMUZ\nQOPHS\nQOPPA\nQORAN\nQORMA\nQOSQO\nQPCRS\nQRAZY\nQSOED\nQSSES\nQTIES\nQTPOC\nQUABS\nQUACH\nQUACK\nQUADE\nQUADI\nQUADS\nQUAFF\nQUAGS\nQUAID\nQUAIL\nQUAKE\nQUAKY\nQUALE\nQUALM\nQUALS\nQUAMS\nQUANS\nQUANT\nQUARE\nQUARG\nQUARK\nQUARL\nQUARS\nQUART\nQUASH\nQUASI\nQUASS\nQUAST\nQUATA\nQUATE\nQUATS\nQUAVE\nQUAYD\nQUAYE\nQUAYS\nQUBIT\nQUDIT\nQUDSI\nQUEAL\nQUEAN\nQUEEB\nQUEEF\nQUEEK\nQUEEN\nQUEEP\nQUEER\nQUEGH\nQUELL\nQUEME\nQUENE\nQUENK\nQUERK\nQUERL\nQUERN\nQUERO\nQUERY\nQUESO\nQUEST\nQUEUE\nQUEYN\nQUEYS\nQUHAR\nQUHAT\nQUHEN\nQUHER\nQUHOW\nQUIBS\nQUICA\nQUICE\nQUICH\nQUICK\nQUIDS\nQUIET\nQUIFF\nQUIGG\nQUILL\nQUILT\nQUIMP\nQUIMS\nQUINE\nQUINK\nQUINN\nQUINS\nQUINT\nQUIPO\nQUIPS\nQUIPU\nQUIRE\nQUIRK\nQUIRL\nQUIRN\nQUIRT\nQUISM\nQUIST\nQUITE\nQUITO\nQUITS\nQUIZZ\nQULFI\nQULIN\nQULIS\nQUMIS\nQUMIX\nQUOAD\nQUOBS\nQUODS\nQUOGS\nQUOIF\nQUOIL\nQUOIN\nQUOIT\nQUOKE\nQUOLL\nQUONK\nQUONS\nQUOOK\nQUOPS\nQUORA\nQUORN\nQUOTA\nQUOTE\nQUOTH\nQUOYS\nQURAN\nQURSH\nQUYAS\nQWORD\nQYOOT\nRAABE\nRAADS\nRAAGS\nRAAHE\nRAATZ\nRAAYA\nRABAB\nRABAT\nRABBI\nRABEL\nRABER\nRABES\nRABHA\nRABIA\nRABIC\nRABID\nRABIN\nRABIP\nRABIS\nRABIZ\nRABON\nRABOT\nRABUN\nRABYS\nRACCA\nRACCS\nRACED\nRACER\nRACES\nRACHE\nRACKA\nRACKS\nRACON\nRACWA\nRADAR\nRADAS\nRADAY\nRADDE\nRADEL\nRADER\nRADES\nRADGE\nRADHA\nRADHI\nRADIF\nRADII\nRADIN\nRADIO\nRADIX\nRADKE\nRADLE\nRADON\nRADOS\nRADYS\nRAFFA\nRAFFE\nRAFFI\nRAFFS\nRAFIE\nRAFIQ\nRAFSI\nRAFTE\nRAFTS\nRAFTY\nRAGAM\nRAGAN\nRAGAS\nRAGED\nRAGER\nRAGES\nRAGEY\nRAGGA\nRAGGY\nRAGHU\nRAGIN\nRAGLE\nRAGON\nRAGOO\nRAGOS\nRAGOU\nRAGUS\nRAHAB\nRAHAL\nRAHES\nRAHIM\nRAHNS\nRAHUI\nRAHUL\nRAIAS\nRAIDS\nRAIGN\nRAIKS\nRAILE\nRAILS\nRAIMS\nRAINA\nRAINE\nRAING\nRAINS\nRAINY\nRAION\nRAIPS\nRAISA\nRAISE\nRAITA\nRAITE\nRAJAB\nRAJAH\nRAJAN\nRAJAS\nRAJID\nRAJIV\nRAJMA\nRAKAI\nRAKED\nRAKEE\nRAKER\nRAKES\nRAKHI\nRAKIA\nRAKIS\nRAKKA\nRAKOW\nRAKSI\nRALES\nRALEY\nRALLO\nRALLS\nRALLY\nRALPH\nRAMAL\nRAMAN\nRAMBO\nRAMEA\nRAMED\nRAMEE\nRAMEN\nRAMER\nRAMES\nRAMET\nRAMEX\nRAMEY\nRAMIC\nRAMIE\nRAMIN\nRAMMY\nRAMPS\nRAMUS\nRANCE\nRANCH\nRANCK\nRANDO\nRANDS\nRANDY\nRANEE\nRANEY\nRANGA\nRANGE\nRANGO\nRANGY\nRANID\nRANIS\nRANKE\nRANKO\nRANKS\nRANNS\nRANNY\nRANOO\nRANTS\nRANTY\nRANTZ\nRANUA\nRAPAN\nRAPED\nRAPEE\nRAPER\nRAPES\nRAPEY\nRAPHE\nRAPID\nRAPOS\nRAPPA\nRAPSO\nRAPTS\nRAQQA\nRARED\nRAREE\nRARER\nRARES\nRARFS\nRASAM\nRASAS\nRASCH\nRASCO\nRASED\nRASES\nRASHI\nRASHY\nRASIC\nRASKA\nRASON\nRASOR\nRASOS\nRASPS\nRASPY\nRASSE\nRASTA\nRASUL\nRATAN\nRATAS\nRATCH\nRATED\nRATEE\nRATEL\nRATER\nRATES\nRATHE\nRATHO\nRATHS\nRATIO\nRATNA\nRATON\nRATOS\nRATTI\nRATTO\nRATTY\nRATUS\nRAUCH\nRAUCO\nRAUDA\nRAUEN\nRAUFS\nRAUHS\nRAUMA\nRAUPP\nRAVAL\nRAVED\nRAVEL\nRAVEN\nRAVER\nRAVES\nRAVEY\nRAVIN\nRAWAP\nRAWER\nRAWKS\nRAWLY\nRAXED\nRAXES\nRAXLE\nRAYAH\nRAYAN\nRAYAS\nRAYED\nRAYES\nRAYLE\nRAYLS\nRAYMO\nRAYNA\nRAYNE\nRAYON\nRAYOS\nRAZAS\nRAZED\nRAZEE\nRAZER\nRAZES\nRAZON\nRAZOO\nRAZOR\nRAZOS\nRBOCS\nRCTIS\nRDBMS\nRDNAS\nRDRAM\nREAAL\nREACH\nREACT\nREADD\nREADE\nREADS\nREADY\nREAFS\nREAIM\nREAIR\nREAIS\nREAKS\nREALE\nREALM\nREALS\nREAME\nREAMS\nREANS\nREAPS\nREAPT\nREARD\nREARM\nREARS\nREASK\nREAST\nREATA\nREAVE\nREAYS\nREBAB\nREBAG\nREBAR\nREBBE\nREBEC\nREBED\nREBEL\nREBER\nREBID\nREBIN\nREBIT\nREBOP\nREBOX\nREBUD\nREBUS\nREBUT\nREBUY\nRECAM\nRECAN\nRECAP\nRECCE\nRECCY\nRECIO\nRECIT\nRECKS\nRECON\nRECTA\nRECTE\nRECTI\nRECTO\nRECUR\nRECUT\nREDAN\nREDAS\nREDDS\nREDDY\nREDER\nREDES\nREDEX\nREDIA\nREDIC\nREDID\nREDIE\nREDIF\nREDIG\nREDIP\nREDLY\nREDON\nREDOS\nREDOX\nREDRY\nREDUB\nREDUG\nREDUX\nREDYE\nREEBS\nREECE\nREECH\nREEDE\nREEDS\nREEDY\nREEFS\nREEFY\nREEKS\nREEKY\nREELS\nREELY\nREEMS\nREENS\nREESE\nREESK\nREEST\nREETH\nREETZ\nREEVE\nREFAN\nREFAX\nREFED\nREFEL\nREFER\nREFFO\nREFIT\nREFIX\nREFLY\nREFRY\nREFTS\nREGAL\nREGAN\nREGAS\nREGEN\nREGER\nREGET\nREGEX\nREGIO\nREGLE\nREGMA\nREGNA\nREGOS\nREGOT\nREGUR\nREHAB\nREHAK\nREHAL\nREHAT\nREHEM\nREHEW\nREHID\nREHMS\nREICH\nREIDY\nREIFF\nREIFY\nREIGN\nREIKI\nREIKO\nREILS\nREILY\nREIMS\nREINK\nREINS\nREIRD\nREISH\nREISM\nREIST\nREISZ\nREITH\nREITZ\nREIVE\nREJET\nREJIG\nREJOG\nREJOY\nREKEY\nRELAX\nRELAY\nRELET\nRELIC\nRELIT\nRELLA\nRELLO\nRELLY\nRELOG\nRELOS\nREMAN\nREMAP\nREMER\nREMET\nREMEW\nREMEX\nREMFS\nREMIC\nREMIS\nREMIT\nREMIX\nREMOP\nREMOW\nREMPE\nREMUS\nRENAL\nRENAY\nRENCH\nRENDA\nRENDE\nRENDS\nRENEE\nRENEW\nRENGA\nRENGO\nRENIG\nRENIN\nRENJU\nRENKS\nRENNA\nRENNE\nRENNS\nRENOD\nRENOS\nRENTE\nRENTS\nRENTZ\nREOIL\nREONG\nREOPS\nREORG\nREPAT\nREPAY\nREPEG\nREPEL\nREPEW\nREPIN\nREPKA\nREPKO\nREPLA\nREPLY\nREPOD\nREPOP\nREPOS\nREPOT\nREPPS\nREPRO\nREPUB\nREPUG\nREPUT\nRERAN\nRERES\nRERIG\nRERUB\nRERUN\nRESAT\nRESAW\nRESAY\nRESCH\nRESED\nRESEE\nRESEL\nRESEN\nRESER\nRESES\nRESET\nRESEW\nRESIA\nRESIN\nRESIP\nRESIT\nRESOD\nRESOW\nRESPA\nRESPS\nRESTO\nRESTS\nRESTY\nRESUB\nRESUE\nRESUM\nRESUS\nRETAG\nRETAP\nRETAR\nRETAX\nRETCH\nRETEE\nRETEM\nRETES\nRETEX\nRETHE\nRETIA\nRETIC\nRETIE\nRETIP\nRETOX\nRETRO\nRETRY\nREUEL\nREULE\nREUME\nREUNE\nREUPS\nREUSE\nREVAL\nREVEL\nREVER\nREVES\nREVET\nREVIE\nREVIS\nREVOW\nREVUE\nREVVY\nREWAX\nREWED\nREWET\nREWIN\nREWIS\nREWLE\nREWON\nREXES\nREYER\nREYES\nREYEZ\nREYNS\nREYSE\nREYSH\nREZAC\nREZAS\nREZES\nREZIP\nRFIDS\nRFLPS\nRHABD\nRHADE\nRHAME\nRHEAN\nRHEAS\nRHEES\nRHEIC\nRHEID\nRHEIN\nRHEME\nRHEMS\nRHETT\nRHEUM\nRHEWS\nRHIAN\nRHIBS\nRHIME\nRHINE\nRHINO\nRHODA\nRHODE\nRHODO\nRHODY\nRHOMB\nRHONA\nRHONE\nRHOVA\nRHUDY\nRHUES\nRHUMB\nRHYME\nRHYNE\nRHYTA\nRIAAN\nRIADS\nRIALS\nRIANT\nRIATA\nRIBAT\nRIBBE\nRIBBY\nRIBIN\nRICCA\nRICCO\nRICED\nRICER\nRICES\nRICHE\nRICHY\nRICIN\nRICKE\nRICKI\nRICKS\nRICKY\nRIDED\nRIDEN\nRIDER\nRIDES\nRIDGE\nRIDGY\nRIDIC\nRIEBE\nRIECK\nRIEDL\nRIEDY\nRIEHL\nRIEKE\nRIELS\nRIEMS\nRIERA\nRIETH\nRIETI\nRIFAS\nRIFED\nRIFER\nRIFFS\nRIFFY\nRIFLE\nRIFTS\nRIGAN\nRIGBY\nRIGEL\nRIGGI\nRIGGS\nRIGHT\nRIGID\nRIGOL\nRIGOR\nRIHAS\nRIJOS\nRIKER\nRIKES\nRILED\nRILES\nRILEY\nRILIE\nRILLA\nRILLE\nRILLS\nRILLY\nRIMAE\nRIMED\nRIMEL\nRIMER\nRIMES\nRIMIC\nRIMUS\nRINCK\nRINDS\nRINDY\nRINED\nRINER\nRINES\nRINEY\nRINGE\nRINGO\nRINGS\nRINGY\nRINIS\nRINKE\nRINKS\nRINNS\nRINOS\nRINSE\nRIOJA\nRIONI\nRIOTS\nRIPED\nRIPEN\nRIPER\nRIPES\nRIPON\nRIPPS\nRIPPY\nRISAN\nRISCH\nRISCS\nRISED\nRISEL\nRISEN\nRISER\nRISES\nRISHI\nRISKE\nRISKO\nRISKS\nRISKY\nRISON\nRISOS\nRISPS\nRISTS\nRITCH\nRITER\nRITES\nRITHE\nRITHS\nRITSA\nRITUX\nRITZY\nRIUER\nRIVAL\nRIVAS\nRIVED\nRIVEL\nRIVEN\nRIVER\nRIVES\nRIVET\nRIVKA\nRIVNE\nRIWON\nRIXON\nRIYAL\nRIYAZ\nRIYOS\nRIZAL\nRIZER\nRIZKS\nRIZLA\nRIZOM\nRIZVI\nRIZZA\nRIZZI\nRMSES\nRNASE\nRNTPS\nRNUTR\nROACH\nROADS\nROAMS\nROANA\nROANE\nROANS\nROAPS\nROARK\nROARS\nROAST\nROATH\nROATS\nROAVE\nROBAB\nROBAK\nROBBI\nROBBO\nROBBS\nROBBY\nROBED\nROBEL\nROBES\nROBEY\nROBIE\nROBIN\nROBLE\nROBOT\nROBYN\nROCAS\nROCCO\nROCHA\nROCHE\nROCKS\nROCKY\nROCOA\nROCTA\nRODDA\nRODDS\nRODDY\nRODEN\nRODEO\nRODER\nRODES\nRODGE\nROEHL\nROEHM\nROEHR\nROELL\nROENA\nROENS\nROGAN\nROGEL\nROGEN\nROGER\nROGET\nROGGE\nROGOR\nROGUE\nROGUY\nROHAN\nROHDE\nROHES\nROHLS\nROHNS\nROHOB\nROHUS\nROIAL\nROICC\nROIDS\nROILS\nROILY\nROINS\nROIST\nROJAK\nROJOS\nROKER\nROKES\nROKOS\nROLAG\nROLAP\nROLED\nROLEN\nROLES\nROLEX\nROLEY\nROLFE\nROLFS\nROLIN\nROLLA\nROLLO\nROLLS\nROLLY\nROLON\nROLPH\nROMAL\nROMAN\nROMAS\nROMBO\nROMEO\nROMER\nROMES\nROMEX\nROMIC\nROMIG\nROMPS\nROMPU\nROMPY\nRONAN\nRONDA\nRONDE\nRONDO\nRONEO\nRONES\nRONEY\nRONGA\nRONIN\nRONKO\nRONKS\nRONNY\nRONTS\nROODS\nROODY\nROOFE\nROOFS\nROOFY\nROOKE\nROOKS\nROOKY\nROOLS\nROOME\nROOMS\nROOMY\nROOPS\nROOPY\nROOSE\nROOST\nROOTS\nROOTY\nROPAR\nROPED\nROPER\nROPES\nROPEY\nROQUE\nRORAL\nRORIC\nRORID\nRORIE\nROROS\nRORTS\nRORTY\nROSAL\nROSAS\nROSCA\nROSED\nROSEL\nROSEN\nROSER\nROSES\nROSET\nROSID\nROSIE\nROSIN\nROSSO\nROSTI\nROSTS\nROTAL\nROTAN\nROTAS\nROTED\nROTES\nROTFL\nROTHE\nROTIS\nROTLS\nROTON\nROTOR\nROTTA\nROTTS\nROTTY\nROTZO\nROUEN\nROUES\nROUET\nROUFS\nROUGE\nROUGH\nROULE\nROUND\nROUNS\nROUPE\nROUPS\nROUPY\nROUSE\nROUSH\nROUST\nROUTE\nROUTH\nROUTS\nROUTT\nROUZE\nROVAL\nROVED\nROVER\nROVES\nROVNO\nROWAN\nROWDY\nROWED\nROWEL\nROWEN\nROWER\nROWIE\nROWKA\nROWSE\nROWTS\nROYAL\nROYCE\nROYES\nROYLE\nROYSE\nROZAS\nROZEK\nROZOS\nROZZI\nRPERS\nRPGER\nRRATS\nRRAUP\nRRNAS\nRRSPS\nRSEOH\nRSFSR\nRSMLT\nRSPCA\nRSVPS\nRTECS\nRTSED\nRTSES\nRTTYS\nRUACH\nRUAKH\nRUANA\nRUANE\nRUANO\nRUANS\nRUARK\nRUBAB\nRUBAI\nRUBEN\nRUBES\nRUBIN\nRUBIO\nRUBLE\nRUBLI\nRUBLU\nRUBOR\nRUBUS\nRUCCI\nRUCHE\nRUCKS\nRUDAS\nRUDDS\nRUDDY\nRUDEN\nRUDER\nRUDGE\nRUDIE\nRUDIN\nRUDRA\nRUEDA\nRUEHL\nRUELS\nRUFFE\nRUFFO\nRUFFS\nRUFFY\nRUFIE\nRUFOL\nRUFUS\nRUGAE\nRUGAL\nRUGBY\nRUGEN\nRUGER\nRUGES\nRUGGS\nRUGHS\nRUGIA\nRUGIN\nRUHLS\nRUHNU\nRUIBE\nRUINE\nRUING\nRUINS\nRUISM\nRUIST\nRUJAK\nRUKHS\nRULED\nRULER\nRULES\nRULEY\nRULEZ\nRULLO\nRUMAL\nRUMBA\nRUMBO\nRUMEN\nRUMER\nRUMEX\nRUMLY\nRUMMY\nRUMOR\nRUMPH\nRUMPS\nRUMPY\nRUNCH\nRUNDI\nRUNDS\nRUNED\nRUNER\nRUNES\nRUNET\nRUNGS\nRUNGU\nRUNIC\nRUNKS\nRUNNS\nRUNNY\nRUNTE\nRUNTS\nRUNTY\nRUNUP\nRUNZA\nRUOFF\nRUPAR\nRUPEE\nRUPEL\nRUPES\nRUPIA\nRUPIS\nRUPLE\nRUPPE\nRUPPS\nRURAL\nRURUS\nRUSCH\nRUSED\nRUSES\nRUSHY\nRUSKS\nRUSKY\nRUSMA\nRUSSO\nRUSTO\nRUSTS\nRUSTY\nRUSUL\nRUSYN\nRUTAN\nRUTIN\nRUTTY\nRUTUL\nRUVID\nRUYLE\nRVING\nRWNJS\nRYALL\nRYALS\nRYANN\nRYANS\nRYBAK\nRYBAS\nRYDEN\nRYDER\nRYERS\nRYGHT\nRYKER\nRYLAN\nRYLEE\nRYLEN\nRYLES\nRYLEY\nRYLIE\nRYMAN\nRYMER\nRYNDS\nRYNGE\nRYONS\nRYOTS\nRYPES\nRYSER\nRYTHS\nSAADH\nSAADS\nSAAFA\nSAALA\nSAALE\nSAAMI\nSABAH\nSABAL\nSABAN\nSABAR\nSABAT\nSABBS\nSABEL\nSABER\nSABHA\nSABIA\nSABIC\nSABIN\nSABIO\nSABIR\nSABJI\nSABLE\nSABLY\nSABOL\nSABOS\nSABOT\nSABRA\nSABRE\nSABZI\nSACAE\nSACAR\nSACBE\nSACCI\nSACCO\nSACDS\nSACHS\nSACKS\nSACKT\nSACRA\nSACRE\nSACTO\nSADAS\nSADDO\nSADED\nSADEK\nSADES\nSADET\nSADHE\nSADHS\nSADHU\nSADIA\nSADIC\nSADIE\nSADIK\nSADIQ\nSADIS\nSADLY\nSADZA\nSAEED\nSAEKO\nSAENZ\nSAETA\nSAFAR\nSAFED\nSAFEN\nSAFER\nSAFES\nSAFFA\nSAFFI\nSAFFY\nSAFIS\nSAFRE\nSAFWA\nSAGAN\nSAGAR\nSAGAS\nSAGED\nSAGER\nSAGES\nSAGEY\nSAGGY\nSAGOS\nSAGUM\nSAGUN\nSAHAR\nSAHAS\nSAHDS\nSAHEB\nSAHEL\nSAHIB\nSAHIH\nSAHIN\nSAHLI\nSAHMS\nSAHNI\nSAHOS\nSAHPS\nSAHRS\nSAHUI\nSAHUL\nSAHUR\nSAHUS\nSAIAS\nSAICE\nSAICK\nSAICS\nSAIDA\nSAIDE\nSAIDS\nSAIES\nSAIFS\nSAIGA\nSAIGE\nSAIKI\nSAILE\nSAILS\nSAILY\nSAIMS\nSAINE\nSAINS\nSAINT\nSAINZ\nSAIPH\nSAIST\nSAITH\nSAITO\nSAIVA\nSAJOU\nSAKAI\nSAKAS\nSAKAU\nSAKER\nSAKES\nSAKHA\nSAKIA\nSAKIS\nSAKRA\nSAKTA\nSAKTI\nSAKWA\nSALAD\nSALAF\nSALAH\nSALAK\nSALAL\nSALAM\nSALAS\nSALAT\nSALAZ\nSALCE\nSALEB\nSALEH\nSALEM\nSALEP\nSALES\nSALET\nSALIB\nSALIC\nSALII\nSALIM\nSALIX\nSALLA\nSALLE\nSALLY\nSALMA\nSALMI\nSALMO\nSALMS\nSALOL\nSALON\nSALOP\nSALOU\nSALPA\nSALPS\nSALSA\nSALSE\nSALTA\nSALTS\nSALTY\nSALTZ\nSALVA\nSALVE\nSALVI\nSALVO\nSAMAD\nSAMAJ\nSAMAN\nSAMAR\nSAMAS\nSAMBA\nSAMBO\nSAMEE\nSAMEK\nSAMEY\nSAMFU\nSAMIC\nSAMIR\nSAMIS\nSAMMI\nSAMMO\nSAMMY\nSAMOA\nSAMOS\nSAMPA\nSAMPI\nSAMPS\nSAMRA\nSANAA\nSANDA\nSANDE\nSANDO\nSANDS\nSANDT\nSANDY\nSANER\nSANFL\nSANGA\nSANGO\nSANGU\nSANHE\nSANJO\nSANKS\nSANKY\nSANMA\nSANNA\nSANNY\nSANON\nSANOS\nSANOW\nSANSA\nSANTA\nSANTI\nSANTS\nSANTY\nSANUR\nSANYA\nSANZA\nSAOLA\nSAONE\nSAPAN\nSAPID\nSAPOL\nSAPOR\nSAPPS\nSAPPY\nSARAF\nSARAH\nSARAI\nSARAN\nSARAY\nSARDO\nSARDS\nSAREE\nSARGE\nSARGO\nSARIC\nSARIN\nSARIS\nSARKS\nSARKY\nSARMA\nSARNA\nSARNS\nSAROD\nSAROH\nSAROI\nSARON\nSAROS\nSAROT\nSARPE\nSARPY\nSARRE\nSARRO\nSARRS\nSARSA\nSARSE\nSARSI\nSARTI\nSARTS\nSARVE\nSARVO\nSARWA\nSARZA\nSASAE\nSASAK\nSASER\nSASES\nSASHA\nSASIN\nSASSE\nSASSO\nSASSY\nSATAI\nSATAN\nSATAY\nSATCH\nSATED\nSATEM\nSATER\nSATES\nSATIN\nSATOS\nSATUN\nSATYR\nSAUBA\nSAUCE\nSAUCY\nSAUDI\nSAUER\nSAUGA\nSAUKS\nSAULE\nSAULS\nSAULT\nSAUNA\nSAUNF\nSAUNG\nSAUNS\nSAURY\nSAUTE\nSAUVE\nSAVAK\nSAVED\nSAVER\nSAVES\nSAVIN\nSAVOR\nSAVOY\nSAVVA\nSAVVY\nSAWAH\nSAWCE\nSAWCY\nSAWED\nSAWER\nSAWIN\nSAWNY\nSAXBY\nSAXED\nSAXES\nSAXIS\nSAXON\nSAYDE\nSAYED\nSAYEN\nSAYER\nSAYES\nSAYID\nSAYON\nSAYRE\nSAYSO\nSAYST\nSAYTH\nSAZAN\nSAZES\nSCABS\nSCADA\nSCADS\nSCAGS\nSCALA\nSCALD\nSCALE\nSCALF\nSCALI\nSCALL\nSCALP\nSCALY\nSCAMP\nSCAMS\nSCANO\nSCANS\nSCANT\nSCAPE\nSCAPI\nSCAPS\nSCARD\nSCARE\nSCARF\nSCARI\nSCARN\nSCARP\nSCARS\nSCART\nSCARY\nSCATE\nSCATH\nSCATS\nSCATT\nSCAUP\nSCAUR\nSCAVO\nSCAWS\nSCBAS\nSCBUS\nSCDMA\nSCDNA\nSCEAS\nSCEAT\nSCELP\nSCENA\nSCEND\nSCENE\nSCENT\nSCERN\nSCHAD\nSCHAH\nSCHAK\nSCHAV\nSCHER\nSCHMO\nSCHON\nSCHOR\nSCHOW\nSCHUG\nSCHUM\nSCHUR\nSCHUT\nSCHWA\nSCHWI\nSCIDS\nSCIEN\nSCIMA\nSCINK\nSCION\nSCIOT\nSCISE\nSCISM\nSCISS\nSCITA\nSCJDS\nSCJPS\nSCLAV\nSCOAT\nSCOBE\nSCOBS\nSCOBY\nSCOFF\nSCOKE\nSCOLA\nSCOLD\nSCOMM\nSCOMO\nSCONE\nSCONS\nSCOON\nSCOOP\nSCOOT\nSCOPA\nSCOPE\nSCOPS\nSCOPY\nSCORE\nSCORM\nSCORN\nSCOSS\nSCOTE\nSCOTS\nSCOTT\nSCOUR\nSCOUT\nSCOVE\nSCOWL\nSCOWS\nSCPHN\nSCRAB\nSCRAG\nSCRAM\nSCRAN\nSCRAP\nSCRAT\nSCRAW\nSCRAY\nSCREE\nSCREW\nSCRID\nSCRIM\nSCRIP\nSCRNA\nSCROD\nSCROG\nSCROW\nSCRUB\nSCRUM\nSCUBA\nSCUDI\nSCUDO\nSCUDS\nSCUFF\nSCUFT\nSCUGS\nSCULD\nSCULK\nSCULL\nSCULP\nSCUMS\nSCUNS\nSCUPS\nSCURF\nSCURS\nSCUTA\nSCUTE\nSCUTS\nSCUTT\nSCUZZ\nSCYEN\nSCYES\nSCYLE\nSCYON\nSCYTH\nSDAER\nSDOWN\nSDRAM\nSDRNA\nSDWTS\nSEAGO\nSEAHS\nSEALE\nSEALS\nSEALY\nSEAMS\nSEAMY\nSEANS\nSEARL\nSEARS\nSEASE\nSEATO\nSEATS\nSEAVE\nSEAVY\nSEAZE\nSEBAT\nSEBIC\nSEBID\nSEBIL\nSEBUM\nSECAM\nSECCO\nSECKS\nSECLE\nSECOR\nSECTS\nSECUS\nSEDAN\nSEDER\nSEDES\nSEDEX\nSEDGE\nSEDGY\nSEDNA\nSEDOR\nSEDUM\nSEECH\nSEEDE\nSEEDS\nSEEDY\nSEEIN\nSEEKE\nSEEKH\nSEEKS\nSEELS\nSEELY\nSEEME\nSEEMS\nSEENE\nSEENS\nSEENT\nSEEPS\nSEEPY\nSEERS\nSEERY\nSEESE\nSEEST\nSEETA\nSEETH\nSEEYA\nSEGAL\nSEGAR\nSEGAS\nSEGER\nSEGNI\nSEGNO\nSEGOL\nSEGOS\nSEGOU\nSEGRA\nSEGUE\nSEGUI\nSEHAR\nSEHRA\nSEIBS\nSEIDL\nSEIFS\nSEIGE\nSEIJI\nSEIKO\nSEINE\nSEINS\nSEIPP\nSEIPS\nSEISE\nSEISM\nSEITH\nSEITY\nSEITZ\nSEIZA\nSEIZE\nSEJID\nSEJMS\nSEKOI\nSEKOS\nSELAH\nSELBY\nSELES\nSELFE\nSELFS\nSELFY\nSELIG\nSELKE\nSELKS\nSELLA\nSELLE\nSELLO\nSELLS\nSELLY\nSELMA\nSELPH\nSELVA\nSEMAJ\nSEMAN\nSEMBA\nSEMED\nSEMEE\nSEMEN\nSEMES\nSEMEY\nSEMIC\nSEMIS\nSEMLA\nSEMON\nSEMUR\nSENAT\nSENCE\nSENCH\nSENCO\nSENDE\nSENDS\nSENES\nSENET\nSENEY\nSENFT\nSENGA\nSENGI\nSENGS\nSENIE\nSENKO\nSENNA\nSENNE\nSENOI\nSENOR\nSENSE\nSENSI\nSENTE\nSENTI\nSENTS\nSENTZ\nSENVY\nSEOUL\nSEPAL\nSEPAT\nSEPES\nSEPIA\nSEPIC\nSEPON\nSEPOY\nSEPPO\nSEPTA\nSEPTS\nSEPUH\nSERAC\nSERAI\nSERAL\nSERBS\nSERCA\nSERDA\nSERER\nSERES\nSERFS\nSERGE\nSERIC\nSERIE\nSERIF\nSERIN\nSERIR\nSERIS\nSERJA\nSERMS\nSERNA\nSERON\nSEROW\nSERPS\nSERRS\nSERRY\nSERTS\nSERUE\nSERUM\nSERVE\nSERVO\nSERVS\nSERYL\nSERZH\nSESAY\nSESHA\nSESIA\nSESMA\nSESSA\nSESTA\nSETAE\nSETAL\nSETAR\nSETAS\nSETEE\nSETER\nSETHI\nSETIM\nSETON\nSETOS\nSETTS\nSETTY\nSETUP\nSEUEN\nSEVAN\nSEVEN\nSEVER\nSEVYS\nSEWED\nSEWEL\nSEWEN\nSEWER\nSEWIN\nSEXED\nSEXER\nSEXES\nSEXET\nSEXLY\nSEXPS\nSEXTE\nSEXTO\nSEXTS\nSEYDE\nSEYNT\nSEYON\nSEZEE\nSFAIK\nSFIHA\nSFNAL\nSFSRS\nSGACC\nSGRAM\nSGRNA\nSGROI\nSGROS\nSHABO\nSHABS\nSHABU\nSHACK\nSHADE\nSHADS\nSHADY\nSHAEF\nSHAFI\nSHAFT\nSHAGS\nSHAHI\nSHAHS\nSHAHY\nSHAIK\nSHAIL\nSHAIN\nSHAKA\nSHAKE\nSHAKO\nSHAKU\nSHAKY\nSHALA\nSHALE\nSHALK\nSHALL\nSHALM\nSHALT\nSHALY\nSHAMA\nSHAME\nSHAMP\nSHAMS\nSHAMY\nSHANA\nSHAND\nSHANE\nSHANG\nSHANI\nSHANK\nSHANS\nSHANT\nSHAOS\nSHAPE\nSHAPS\nSHAPT\nSHARD\nSHARE\nSHARI\nSHARK\nSHARM\nSHARN\nSHARP\nSHART\nSHASH\nSHASS\nSHATS\nSHATT\nSHAUB\nSHAUL\nSHAUN\nSHAVE\nSHAWL\nSHAWM\nSHAWN\nSHAWS\nSHAYA\nSHAYE\nSHAYS\nSHCHA\nSHCHI\nSHDNA\nSHEAD\nSHEAF\nSHEAL\nSHEAN\nSHEAR\nSHEAS\nSHEAT\nSHEAY\nSHEBA\nSHECK\nSHEDD\nSHEDS\nSHEEN\nSHEEP\nSHEER\nSHEET\nSHEFA\nSHEFF\nSHEHE\nSHEIK\nSHELD\nSHELF\nSHELL\nSHEMA\nSHEMU\nSHEND\nSHENG\nSHENK\nSHENS\nSHENT\nSHEOL\nSHEPS\nSHERD\nSHERI\nSHERK\nSHERN\nSHERO\nSHERS\nSHETH\nSHETS\nSHEVA\nSHEWA\nSHEWE\nSHEWN\nSHEWS\nSHIAH\nSHIAS\nSHIBE\nSHICK\nSHIDE\nSHIDO\nSHIDS\nSHIED\nSHIEH\nSHIEL\nSHIER\nSHIES\nSHIFE\nSHIFT\nSHIFU\nSHIGA\nSHIHS\nSHIJO\nSHIKO\nSHILD\nSHILF\nSHILL\nSHILY\nSHIMA\nSHIMP\nSHIMS\nSHINA\nSHINE\nSHINK\nSHINN\nSHINS\nSHINY\nSHIOK\nSHIPE\nSHIPP\nSHIPS\nSHIRA\nSHIRE\nSHIRK\nSHIRL\nSHIRO\nSHIRR\nSHIRT\nSHISH\nSHISO\nSHIST\nSHITE\nSHITO\nSHITS\nSHITY\nSHIUR\nSHIUS\nSHIVA\nSHIVE\nSHIVS\nSHIZZ\nSHLEP\nSHLLV\nSHLUB\nSHMIR\nSHMOE\nSHMOO\nSHMUK\nSHMUP\nSHNOR\nSHOAD\nSHOAF\nSHOAH\nSHOAL\nSHOAR\nSHOAT\nSHOBE\nSHOCK\nSHODE\nSHOED\nSHOER\nSHOES\nSHOEY\nSHOFF\nSHOGI\nSHOGS\nSHOJI\nSHOJO\nSHOLA\nSHOLD\nSHOLE\nSHOLL\nSHOMA\nSHOMO\nSHOMU\nSHONA\nSHONE\nSHONK\nSHONS\nSHOOD\nSHOOK\nSHOOL\nSHOON\nSHOOP\nSHOOS\nSHOOT\nSHOPE\nSHOPS\nSHORB\nSHORE\nSHORL\nSHORN\nSHORT\nSHORY\nSHOTA\nSHOTE\nSHOTS\nSHOTT\nSHOUP\nSHOUT\nSHOVE\nSHOWN\nSHOWS\nSHOWY\nSHOYU\nSHRAB\nSHRAG\nSHRAM\nSHRAP\nSHRED\nSHREK\nSHREW\nSHRNA\nSHROG\nSHROW\nSHRUB\nSHRUG\nSHRUM\nSHSPS\nSHTIK\nSHTML\nSHTOF\nSHTUM\nSHTUP\nSHTUS\nSHUAH\nSHUAR\nSHUBI\nSHUCK\nSHUDE\nSHUDO\nSHUDS\nSHUES\nSHUEY\nSHUFF\nSHUGS\nSHUKA\nSHULD\nSHULI\nSHULL\nSHULS\nSHUMS\nSHUNK\nSHUNS\nSHUNT\nSHUPE\nSHUPP\nSHURA\nSHURE\nSHUSH\nSHUTE\nSHUTS\nSHUTT\nSHVAS\nSHWAG\nSHWAS\nSHYER\nSHYLA\nSHYLY\nSIADH\nSIAFU\nSIANG\nSIANO\nSIANY\nSIBEH\nSIBIA\nSIBIU\nSIBYL\nSICAS\nSICCA\nSICEL\nSICES\nSICHS\nSICHT\nSICKE\nSICKO\nSICKS\nSICKY\nSICLE\nSIDAL\nSIDAS\nSIDED\nSIDER\nSIDES\nSIDEY\nSIDHE\nSIDHU\nSIDLE\nSIDON\nSIDOR\nSIDTH\nSIECK\nSIEGE\nSIENA\nSIENS\nSIENT\nSIERS\nSIEVA\nSIEVE\nSIEZE\nSIFAC\nSIFTS\nSIFUS\nSIGGY\nSIGHS\nSIGHT\nSIGIL\nSIGLA\nSIGMA\nSIGNA\nSIGNE\nSIGNS\nSIHRS\nSIIRT\nSIJOS\nSIKAR\nSIKAS\nSIKED\nSIKER\nSIKES\nSIKHI\nSIKHS\nSILAS\nSILAT\nSILDS\nSILED\nSILEO\nSILER\nSILES\nSILEX\nSILKE\nSILKS\nSILKY\nSILLA\nSILLS\nSILLY\nSILOS\nSILTS\nSILTY\nSILVA\nSILYL\nSIMAP\nSIMAR\nSIMAS\nSIMBA\nSIMEK\nSIMES\nSIMIC\nSIMIT\nSIMMS\nSIMON\nSIMPS\nSIMPY\nSIMUL\nSIMUN\nSINAI\nSINCE\nSINCH\nSINDE\nSINDH\nSINDI\nSINES\nSINEW\nSINGE\nSINGH\nSINGS\nSINHA\nSINIC\nSINIK\nSINKO\nSINKS\nSINKT\nSINNE\nSINNY\nSINOP\nSINOR\nSINTA\nSINTI\nSINTO\nSINTU\nSINUS\nSIONS\nSIOUX\nSIPED\nSIPES\nSIPID\nSIPLE\nSIPPS\nSIPPY\nSIRAC\nSIRAS\nSIRED\nSIREE\nSIREN\nSIRES\nSIRIH\nSIRIS\nSIRJI\nSIRMA\nSIRNA\nSIROC\nSIRON\nSIROP\nSIRTE\nSIRTF\nSIRUP\nSIRVA\nSISAK\nSISAL\nSISAM\nSISCO\nSISEL\nSISES\nSISIG\nSISKA\nSISKS\nSISON\nSISSY\nSISTA\nSISTI\nSISTS\nSITAR\nSITCH\nSITED\nSITES\nSITHE\nSITHS\nSITKO\nSITUP\nSITUS\nSIVAK\nSIVAN\nSIVER\nSIXER\nSIXES\nSIXMO\nSIXTE\nSIXTH\nSIXTY\nSIYAN\nSIZAR\nSIZED\nSIZEL\nSIZER\nSIZES\nSIZEY\nSKAAR\nSKAGS\nSKAIL\nSKAIN\nSKALD\nSKALL\nSKANK\nSKARE\nSKARN\nSKART\nSKATE\nSKATS\nSKATT\nSKAWS\nSKEAN\nSKEAR\nSKEDS\nSKEED\nSKEEL\nSKEEN\nSKEER\nSKEES\nSKEET\nSKEGS\nSKEIN\nSKELL\nSKELP\nSKENE\nSKENG\nSKENS\nSKEOS\nSKEPS\nSKETE\nSKETS\nSKEWB\nSKEWS\nSKIBA\nSKIDI\nSKIDS\nSKIED\nSKIER\nSKIES\nSKIEY\nSKIFF\nSKIFT\nSKIIS\nSKILL\nSKILS\nSKIMP\nSKIMS\nSKINK\nSKINS\nSKINT\nSKIOS\nSKIPS\nSKIPT\nSKIRL\nSKIRR\nSKIRT\nSKITE\nSKITS\nSKIVE\nSKLAR\nSKOAL\nSKODA\nSKOGS\nSKOLL\nSKOLS\nSKOLT\nSKOOG\nSKOOL\nSKORT\nSKOSH\nSKOTS\nSKOUT\nSKOWS\nSKRIK\nSKRIM\nSKRRT\nSKUAS\nSKUDS\nSKUED\nSKUES\nSKUGS\nSKULD\nSKULK\nSKULL\nSKUMS\nSKUNK\nSKURF\nSKUTE\nSKYED\nSKYES\nSKYEY\nSKYFS\nSKYLA\nSKYLY\nSKYPE\nSKYTE\nSLABS\nSLABY\nSLACK\nSLADE\nSLAGS\nSLAIE\nSLAIN\nSLAKE\nSLAMA\nSLAMS\nSLANE\nSLANG\nSLANK\nSLANS\nSLANT\nSLAPE\nSLAPP\nSLAPS\nSLAPT\nSLASH\nSLATE\nSLATS\nSLATT\nSLATY\nSLAVA\nSLAVE\nSLAVI\nSLAVS\nSLAWS\nSLAYN\nSLAYS\nSLAZY\nSLBMS\nSLCMS\nSLEAN\nSLEBS\nSLECK\nSLEDD\nSLEDS\nSLEEK\nSLEEP\nSLEES\nSLEET\nSLEID\nSLEMP\nSLENT\nSLEPT\nSLEWS\nSLEYS\nSLICE\nSLICH\nSLICK\nSLIDE\nSLIDY\nSLIER\nSLIGH\nSLIGO\nSLIKE\nSLILY\nSLIME\nSLIMS\nSLIMY\nSLING\nSLINK\nSLIPE\nSLIPS\nSLIPT\nSLISH\nSLITE\nSLITS\nSLIVA\nSLIVE\nSLIWA\nSLOAM\nSLOAN\nSLOAT\nSLOBS\nSLOCK\nSLOES\nSLOGO\nSLOGS\nSLOID\nSLOJD\nSLOKA\nSLOKE\nSLOMO\nSLONE\nSLOOD\nSLOOM\nSLOOP\nSLOOS\nSLOOT\nSLOPE\nSLOPS\nSLOPY\nSLORA\nSLORC\nSLORE\nSLOSH\nSLOTH\nSLOTS\nSLOVE\nSLOWN\nSLOWS\nSLOYD\nSLUBS\nSLUDS\nSLUED\nSLUES\nSLUFF\nSLUFS\nSLUGS\nSLUMP\nSLUMS\nSLUNE\nSLUNG\nSLUNJ\nSLUNK\nSLUNT\nSLURP\nSLURS\nSLUSH\nSLUTS\nSLUTT\nSLYER\nSLYES\nSLYLY\nSLYMY\nSLYPE\nSMAAK\nSMACK\nSMAIL\nSMAKE\nSMALE\nSMALL\nSMALT\nSMARK\nSMARM\nSMARR\nSMART\nSMASH\nSMAZE\nSMBSS\nSMEAC\nSMEAD\nSMEAL\nSMEAR\nSMEES\nSMEGS\nSMELL\nSMELT\nSMERD\nSMERK\nSMEWS\nSMEXY\nSMICK\nSMIDT\nSMIFT\nSMILE\nSMILT\nSMIRK\nSMIRT\nSMITE\nSMITH\nSMITS\nSMITT\nSMIZE\nSMLES\nSMOAK\nSMOCK\nSMOFS\nSMOGS\nSMOKE\nSMOKO\nSMOKY\nSMOLA\nSMOLT\nSMOOR\nSMOOT\nSMORC\nSMORE\nSMOTE\nSMOUT\nSMRNA\nSMSAS\nSMSED\nSMSES\nSMSFS\nSMUGS\nSMUON\nSMURF\nSMURS\nSMUSH\nSMUTS\nSMYTH\nSNACK\nSNAFU\nSNAGS\nSNAIL\nSNAKE\nSNAKY\nSNAPE\nSNAPP\nSNAPS\nSNAPT\nSNARE\nSNARF\nSNARK\nSNARL\nSNARR\nSNARS\nSNARY\nSNASH\nSNAST\nSNATH\nSNAYS\nSNEAD\nSNEAK\nSNEAP\nSNEBS\nSNECK\nSNEDS\nSNEED\nSNEER\nSNEES\nSNELL\nSNERK\nSNERT\nSNETS\nSNEWS\nSNIBS\nSNICE\nSNICK\nSNIDE\nSNIED\nSNIES\nSNIFF\nSNIFT\nSNIGG\nSNIGS\nSNIKT\nSNIPE\nSNIPS\nSNIPY\nSNIRT\nSNITE\nSNITS\nSNITZ\nSNIVE\nSNIZZ\nSNOAD\nSNOBS\nSNODS\nSNOEK\nSNOFF\nSNOGS\nSNOHR\nSNOIL\nSNOKE\nSNOOD\nSNOOF\nSNOOK\nSNOOL\nSNOOP\nSNOOT\nSNORE\nSNORT\nSNOTS\nSNOUT\nSNOWE\nSNOWL\nSNOWN\nSNOWS\nSNOWY\nSNOZE\nSNRIS\nSNRNA\nSNRNP\nSNUBA\nSNUBS\nSNUCK\nSNUFF\nSNUGS\nSNURF\nSNUSS\nSNYES\nSOADY\nSOAKS\nSOAKT\nSOAKY\nSOALS\nSOAMS\nSOANE\nSOAPS\nSOAPY\nSOARS\nSOAVE\nSOBBY\nSOBEK\nSOBEL\nSOBER\nSOBHD\nSOBOL\nSOCAL\nSOCBS\nSOCCA\nSOCHA\nSOCHI\nSOCIA\nSOCIE\nSOCII\nSOCIO\nSOCKO\nSOCKS\nSOCKY\nSOCLE\nSOCOM\nSODAS\nSODDY\nSODER\nSODHI\nSODIC\nSODOM\nSODOR\nSOFAR\nSOFAS\nSOFER\nSOFFI\nSOFIA\nSOFIC\nSOFIS\nSOFKY\nSOFTA\nSOFTS\nSOFTY\nSOFUE\nSOGER\nSOGGY\nSOHAL\nSOHCS\nSOHLS\nSOILE\nSOILS\nSOILY\nSOINI\nSOJER\nSOJKA\nSOKAR\nSOKEN\nSOKES\nSOKOL\nSOKOP\nSOKOS\nSOLAH\nSOLAN\nSOLAR\nSOLDI\nSOLDO\nSOLED\nSOLEI\nSOLEM\nSOLEN\nSOLER\nSOLES\nSOLFA\nSOLID\nSOLIE\nSOLIN\nSOLIS\nSOLIZ\nSOLLY\nSOLON\nSOLOS\nSOLUM\nSOLVE\nSOLVI\nSOLWY\nSOMAJ\nSOMAL\nSOMAS\nSOMEN\nSOMER\nSOMMA\nSOMME\nSOMMY\nSOMON\nSONAR\nSONAS\nSONDE\nSONES\nSONET\nSONGE\nSONGS\nSONGY\nSONIA\nSONIC\nSONIS\nSONJA\nSONJO\nSONLY\nSONNE\nSONNY\nSONSY\nSONYA\nSOOEE\nSOOEY\nSOOJI\nSOOKS\nSOOKY\nSOOLS\nSOONE\nSOONG\nSOONS\nSOOPS\nSOOQS\nSOORD\nSOOTH\nSOOTS\nSOOTY\nSOPER\nSOPES\nSOPHA\nSOPHI\nSOPHS\nSOPHY\nSOPJE\nSOPKO\nSOPOR\nSOPOS\nSOPPY\nSOPVS\nSORAL\nSORAS\nSORBS\nSORCE\nSORDS\nSORED\nSOREE\nSOREL\nSORER\nSORES\nSOREY\nSORFS\nSORGO\nSORGS\nSORIA\nSORIE\nSORNA\nSORNS\nSOROR\nSOROS\nSORRA\nSORRS\nSORRY\nSORTA\nSORTE\nSORTO\nSORTS\nSORUS\nSOSES\nSOTHE\nSOTHO\nSOTOL\nSOTON\nSOTTO\nSOTUS\nSOUCE\nSOUCY\nSOUDA\nSOUGH\nSOUKS\nSOULE\nSOULS\nSOUND\nSOUPS\nSOUPY\nSOUQS\nSOURS\nSOUSA\nSOUSE\nSOUTH\nSOUTO\nSOUZA\nSOVAS\nSOVKI\nSOVOK\nSOWAR\nSOWAS\nSOWCE\nSOWED\nSOWEI\nSOWEN\nSOWER\nSOWLE\nSOWLS\nSOWRE\nSOWSE\nSOWTH\nSOWWY\nSOYLE\nSOYUZ\nSOZAS\nSOZZI\nSPAAD\nSPACE\nSPACK\nSPACY\nSPADA\nSPADE\nSPADO\nSPADS\nSPADY\nSPAES\nSPAFF\nSPAHI\nSPAHN\nSPAHR\nSPAID\nSPAIN\nSPAKE\nSPAKY\nSPALD\nSPALE\nSPALL\nSPALT\nSPAMS\nSPANE\nSPANG\nSPANK\nSPANO\nSPANS\nSPARE\nSPARK\nSPARR\nSPARS\nSPARY\nSPASM\nSPATE\nSPATH\nSPATS\nSPAUG\nSPAVE\nSPAWL\nSPAWN\nSPAWS\nSPAYS\nSPAZA\nSPAZM\nSPAZZ\nSPCUM\nSPEAK\nSPEAN\nSPEAR\nSPECI\nSPECK\nSPECS\nSPECT\nSPEDE\nSPEED\nSPEEL\nSPEER\nSPEET\nSPEIR\nSPEKE\nSPELD\nSPELK\nSPELL\nSPELT\nSPEND\nSPENT\nSPEOI\nSPEOS\nSPERA\nSPERE\nSPERG\nSPERL\nSPERM\nSPERO\nSPESH\nSPETH\nSPETS\nSPEWN\nSPEWS\nSPEWY\nSPHEX\nSPIAL\nSPICA\nSPICE\nSPICK\nSPICS\nSPICY\nSPIDE\nSPIED\nSPIEL\nSPIER\nSPIES\nSPIFE\nSPIFF\nSPIKE\nSPIKY\nSPILE\nSPILL\nSPILT\nSPIME\nSPIMS\nSPINA\nSPINE\nSPING\nSPINK\nSPINO\nSPINS\nSPINY\nSPIRE\nSPIRO\nSPIRT\nSPIRY\nSPISS\nSPITE\nSPITS\nSPITZ\nSPIVA\nSPIVS\nSPLAT\nSPLAY\nSPLIF\nSPLIT\nSPLOG\nSPLOT\nSPOAK\nSPOCK\nSPODS\nSPOFS\nSPOHN\nSPOIL\nSPOKE\nSPOLE\nSPONG\nSPONK\nSPOOF\nSPOOK\nSPOOL\nSPOOM\nSPOON\nSPOOR\nSPORE\nSPORK\nSPORT\nSPOSE\nSPOSH\nSPOTO\nSPOTS\nSPOUT\nSPPSS\nSPRAD\nSPRAG\nSPRAT\nSPRAY\nSPRED\nSPREE\nSPREP\nSPREW\nSPRIG\nSPRIT\nSPROC\nSPROD\nSPROG\nSPROW\nSPRUE\nSPRUG\nSPRYS\nSPUDS\nSPUED\nSPUES\nSPUIS\nSPUME\nSPUMY\nSPUNK\nSPUNS\nSPURN\nSPURR\nSPURS\nSPURT\nSPUTA\nSPUTE\nSPUTS\nSPYAL\nSPYCE\nSPYED\nSPYRE\nSQEPS\nSQRRR\nSQUAB\nSQUAD\nSQUAM\nSQUAT\nSQUAW\nSQUEE\nSQUEG\nSQUIB\nSQUID\nSQUIG\nSQUIR\nSQUIT\nSQUIZ\nSQUOP\nSQUSH\nSRAAM\nSRAMS\nSRANG\nSRBMS\nSREYS\nSRLVS\nSRNAS\nSROKA\nSRSES\nSRSGS\nSRSLY\nSRUTI\nSSADM\nSSADV\nSSBBW\nSSBIS\nSSBNS\nSSDLS\nSSDNA\nSSEES\nSSGNS\nSSHED\nSSHES\nSSHRC\nSSIDS\nSSNRI\nSSRES\nSSRIB\nSSRIS\nSSRNA\nSSRNS\nSSSIS\nSSTHS\nSTAAB\nSTABS\nSTACE\nSTACI\nSTACK\nSTACY\nSTADE\nSTAFF\nSTAGE\nSTAGG\nSTAGS\nSTAGY\nSTAHL\nSTAHR\nSTAIB\nSTAID\nSTAIL\nSTAIN\nSTAIR\nSTAKE\nSTALE\nSTALK\nSTALL\nSTAME\nSTAMP\nSTAMS\nSTAND\nSTANE\nSTANG\nSTANK\nSTANO\nSTANS\nSTAPE\nSTAPH\nSTAPP\nSTAPS\nSTARE\nSTARK\nSTARN\nSTARR\nSTARS\nSTART\nSTARY\nSTASH\nSTASI\nSTATE\nSTATS\nSTATZ\nSTAUS\nSTAVE\nSTAWS\nSTAYS\nSTDIN\nSTEAD\nSTEAK\nSTEAL\nSTEAM\nSTEAN\nSTEAR\nSTECK\nSTECS\nSTEDS\nSTEDY\nSTEED\nSTEEK\nSTEEL\nSTEEM\nSTEEN\nSTEEP\nSTEER\nSTEES\nSTEEZ\nSTEFF\nSTEGS\nSTEHR\nSTEIB\nSTEIK\nSTEIL\nSTEIN\nSTELA\nSTELE\nSTELL\nSTEME\nSTEMM\nSTEMS\nSTEND\nSTENO\nSTENT\nSTEPH\nSTEPP\nSTEPS\nSTEPT\nSTERE\nSTERK\nSTERN\nSTETS\nSTETZ\nSTEVE\nSTEVO\nSTEWS\nSTEWY\nSTEYS\nSTFAN\nSTFEN\nSTFSY\nSTHAL\nSTIAN\nSTICE\nSTICH\nSTICK\nSTIED\nSTIES\nSTIFF\nSTIGS\nSTIHL\nSTIKE\nSTILB\nSTILE\nSTILI\nSTILL\nSTILP\nSTILT\nSTIME\nSTIMS\nSTIMY\nSTING\nSTINK\nSTINT\nSTIOB\nSTIPE\nSTIPP\nSTIPS\nSTIRK\nSTIRP\nSTIRS\nSTITH\nSTITT\nSTIVE\nSTIVY\nSTOAE\nSTOAI\nSTOAK\nSTOAS\nSTOAT\nSTOBS\nSTOCK\nSTOEP\nSTOGA\nSTOGY\nSTOHR\nSTOIC\nSTOKE\nSTOLA\nSTOLE\nSTOLI\nSTOLL\nSTOLN\nSTOLP\nSTOMA\nSTOMP\nSTOND\nSTONE\nSTONG\nSTONK\nSTONY\nSTOOD\nSTOOK\nSTOOL\nSTOOM\nSTOOP\nSTOOR\nSTOPE\nSTOPS\nSTOPT\nSTORE\nSTORK\nSTORM\nSTORY\nSTORZ\nSTOSH\nSTOSS\nSTOTE\nSTOTS\nSTOTT\nSTOTZ\nSTOUK\nSTOUP\nSTOUR\nSTOUT\nSTOVE\nSTOVL\nSTOWE\nSTOWP\nSTOWS\nSTOYS\nSTRAD\nSTRAM\nSTRAP\nSTRAT\nSTRAW\nSTRAY\nSTREB\nSTREP\nSTREW\nSTREY\nSTRIA\nSTRID\nSTRIG\nSTRIM\nSTRIP\nSTRIX\nSTROG\nSTROP\nSTROW\nSTROY\nSTRUB\nSTRUM\nSTRUT\nSTUBS\nSTUCK\nSTUDS\nSTUDT\nSTUDY\nSTUFA\nSTUFF\nSTUFT\nSTUHR\nSTUIE\nSTUKA\nSTUKE\nSTULL\nSTULM\nSTULP\nSTULT\nSTUMP\nSTUMS\nSTUNG\nSTUNK\nSTUNS\nSTUNT\nSTUPA\nSTUPE\nSTURB\nSTURK\nSTURS\nSTURT\nSTUSH\nSTUTS\nSTYCA\nSTYED\nSTYEN\nSTYER\nSTYES\nSTYLE\nSTYLI\nSTYLL\nSTYLO\nSTYME\nSTYMY\nSTYPE\nSUADE\nSUAGE\nSUAKU\nSUANT\nSUAVE\nSUAVO\nSUAZO\nSUBAH\nSUBBY\nSUBER\nSUBIA\nSUBJS\nSUBMM\nSUBZI\nSUCAN\nSUCCI\nSUCHY\nSUCKA\nSUCKS\nSUCKT\nSUCKY\nSUCOS\nSUCRE\nSUDAK\nSUDAN\nSUDDS\nSUDEP\nSUDER\nSUDHA\nSUDIP\nSUDOL\nSUDOR\nSUDRA\nSUDSY\nSUEBI\nSUEDE\nSUENS\nSUENT\nSUERO\nSUERS\nSUEST\nSUETH\nSUETS\nSUETY\nSUEVI\nSUFIC\nSUFIS\nSUGAN\nSUGAR\nSUGGS\nSUGOI\nSUGYA\nSUHRS\nSUHUR\nSUIDS\nSUINE\nSUING\nSUINT\nSUIRE\nSUIST\nSUITE\nSUITS\nSUITT\nSUJEE\nSUJUD\nSUJUK\nSUKEY\nSUKHS\nSUKIE\nSUKIS\nSUKLA\nSUKUK\nSUKUN\nSULAK\nSULCI\nSULEN\nSULEV\nSULFA\nSULID\nSULKS\nSULKY\nSULLS\nSULLY\nSULUS\nSUMAC\nSUMAN\nSUMAT\nSUMER\nSUMET\nSUMIE\nSUMMA\nSUMMY\nSUMON\nSUMOS\nSUMPH\nSUMPS\nSUMPY\nSUMTI\nSUNDA\nSUNDE\nSUNGA\nSUNGS\nSUNIS\nSUNJA\nSUNLY\nSUNNA\nSUNNE\nSUNNI\nSUNNY\nSUNUP\nSUOMI\nSUONA\nSUPER\nSUPES\nSUPPA\nSUPRA\nSURAH\nSURAJ\nSURAL\nSURAS\nSURAT\nSURAU\nSURDS\nSUREN\nSURER\nSURFS\nSURFY\nSURGE\nSURGY\nSURIN\nSURIS\nSURLA\nSURLY\nSURMA\nSUROZ\nSURRA\nSURRY\nSURSY\nSURUC\nSURYA\nSUSAN\nSUSEL\nSUSHI\nSUSIE\nSUSKI\nSUSPS\nSUSSO\nSUSTO\nSUSUS\nSUSYS\nSUTER\nSUTLE\nSUTOR\nSUTRA\nSUTTA\nSUWAR\nSUWON\nSUYAS\nSUYOG\nSUZIE\nSVANS\nSVECS\nSVELT\nSVETA\nSVOIP\nSWABE\nSWABS\nSWABY\nSWACK\nSWADS\nSWAGE\nSWAGS\nSWAIM\nTROWL\nTROWS\nTRRNA\nTRSES\nTRUAR\nTRUAX\nTRUBS\nTRUBY\nTRUCE\nTRUCK\nTRUDI\nTRUDY\nTRUED\nTRUEL\nTRUER\nTRUES\nTRUEX\nTRUGG\nTRUGS\nTRUKU\nTRULL\nTRULY\nTRUMP\nTRUNK\nTRURO\nTRUSS\nTRUST\nTRUTH\nTRUTI\nTRYAL\nTRYED\nTRYER\nTRYMA\nTRYNA\nTRYON\nTRYST\nTRYTE\nTSADE\nTSADI\nTSAIS\nTSAMA\nTSANG\nTSANS\nTSAOS\nTSARS\nTSCHK\nTSEBE\nTSENG\nTSHEG\nTSINE\nTSKED\nTSMVS\nTSOIS\nTSOPK\nTSUBA\nTSUBO\nTSUGA\nTSUIS\nTSUJI\nTSUNA\nTSUNS\nTSZUJ\nTTBAR\nTTEOK\nTTHMS\nTTIPS\nTUANS\nTUART\nTUATH\nTUBAE\nTUBAL\nTUBAR\nTUBAS\nTUBAX\nTUBBO\nTUBBS\nTUBBY\nTUBED\nTUBER\nTUBES\nTUBOG\nTUCAN\nTUCET\nTUCKS\nTUCUM\nTUDOR\nTUDUN\nTUELL\nTUELS\nTUFAS\nTUFFS\nTUFTS\nTUFTY\nTUGGY\nTUGRA\nTUILE\nTUINA\nTUISM\nTUITE\nTUITS\nTUJIA\nTUKEY\nTUKUL\nTULES\nTULEY\nTULIA\nTULIP\nTULKA\nTULKU\nTULLE\nTULLS\nTULLY\nTULOU\nTULPA\nTULSA\nTULSI\nTUMAH\nTUMBI\nTUMEN\nTUMEY\nTUMID\nTUMIS\nTUMMO\nTUMMY\nTUMOR\nTUMPS\nTUMPY\nTUNAS\nTUNED\nTUNER\nTUNES\nTUNGA\nTUNGS\nTUNIC\nTUNIS\nTUNKS\nTUNKU\nTUNNY\nTUOBA\nTUOHY\nTUPAN\nTUPAS\nTUPEK\nTUPIK\nTUPLE\nTUPOS\nTUPOU\nTUQUE\nTURAN\nTURAY\nTURBO\nTURCA\nTURCO\nTURDS\nTURDY\nTURFS\nTURFY\nTURGS\nTURIA\nTURIN\nTURIO\nTURIS\nTURKE\nTURKO\nTURKS\nTURKU\nTURME\nTURMS\nTURNS\nTURNT\nTURPS\nTURRA\nTURSI\nTURUQ\nTUSAS\nTUSHY\nTUSKS\nTUSKY\nTUTEE\nTUTEN\nTUTES\nTUTIN\nTUTOR\nTUTOY\nTUTSI\nTUTTI\nTUTTS\nTUTTY\nTUTUS\nTUVAN\nTUXED\nTUXES\nTUXIE\nTUYAS\nTUYER\nTUZLA\nTWAIN\nTWANG\nTWANK\nTWATS\nTWEAG\nTWEAK\nTWEDT\nTWEEB\nTWEED\nTWEEK\nTWEEL\nTWEEN\nTWEEP\nTWEER\nTWEET\nTWELL\nTWERK\nTWERP\nTWICE\nTWICT\nTWIER\nTWIGG\nTWIGS\nTWILL\nTWILT\nTWIMC\nTWINE\nTWINK\nTWINS\nTWINY\nTWIPS\nTWIRE\nTWIRK\nTWIRL\nTWIRP\nTWISP\nTWIST\nTWITE\nTWITS\nTWIXT\nTWOCS\nTWOER\nTWOMP\nTWONK\nTWOTE\nTWOTH\nTWOTY\nTWUNT\nTWYER\nTXTED\nTYAGI\nTYAPS\nTYCHE\nTYCHO\nTYDES\nTYEES\nTYEKS\nTYERS\nTYETS\nTYGER\nTYING\nTYIYN\nTYKES\nTYLED\nTYLER\nTYLES\nTYLID\nTYLKA\nTYMES\nTYMPS\nTYNAN\nTYNED\nTYNER\nTYNES\nTYPAL\nTYPED\nTYPER\nTYPES\nTYPEY\nTYPIC\nTYPOS\nTYRAN\nTYRED\nTYREE\nTYRES\nTYROL\nTYRON\nTYROS\nTYRUS\nTYSON\nTYTHE\nTZADI\nTZARS\nUARTS\nUBACS\nUBAID\nUBEDA\nUBERS\nUBERT\nUBUME\nUBYKH\nUCAVS\nUCCJA\nUCHEE\nUCONN\nUDALF\nUDALL\nUDALS\nUDAND\nUDDER\nUDDIN\nUDELL\nUDEPT\nUDERT\nUDINE\nUDOLL\nUDONS\nUDPGA\nUDULT\nUDUPI\nUEDAS\nUELEN\nUFFDA\nUFTAA\nUGALI\nUGALS\nUGARI\nUGGED\nUGGER\nUGGLE\nUGGOS\nUGMOS\nUGRIC\nUHART\nUHERS\nUHHUH\nUHLAN\nUHLER\nUHLIG\nUHRIG\nUIGGS\nUIGUR\nUIIDS\nUINAL\nUKASE\nUKIAH\nUKIES\nUKIRT\nUKONU\nUKSSR\nUKWMO\nULAID\nULAMA\nULANS\nULCCS\nULCER\nULCUS\nULDBS\nULDLS\nULEMA\nULERY\nULING\nULIRG\nULLAH\nULLET\nULLOA\nULLOM\nULMAN\nULMIC\nULMIN\nULNAD\nULNAE\nULNAR\nULNAS\nULOCS\nULOID\nULPAN\nULREY\nULRIC\nULSAN\nULTRA\nULUIT\nULURU\nUMALI\nUMAMI\nUMANA\nUMARA\nUMARI\nUMARS\nUMASS\nUMBEL\nUMBER\nUMBOS\nUMBRA\nUMBRE\nUMBRI\nUMEDA\nUMEKO\nUMIAC\nUMIAK\nUMIAQ\nUMIAT\nUMICH\nUMIKO\nUMIST\nUMMAH\nUMMAS\nUMMED\nUMMON\nUMOCS\nUMPAN\nUMPED\nUMPPS\nUMPTY\nUMQAN\nUMRAH\nUNADD\nUNAGI\nUNAIS\nUNAMI\nUNANI\nUNAPT\nUNARC\nUNARM\nUNARY\nUNASK\nUNASS\nUNATE\nUNAUS\nUNBAG\nUNBAN\nUNBAR\nUNBAY\nUNBED\nUNBET\nUNBID\nUNBIT\nUNBOW\nUNBOX\nUNBOY\nUNCAL\nUNCAN\nUNCAP\nUNCIA\nUNCLE\nUNCLY\nUNCOY\nUNCTS\nUNCUP\nUNCUS\nUNCUT\nUNDAM\nUNDEF\nUNDER\nUNDEX\nUNDID\nUNDIE\nUNDIG\nUNDMS\nUNDOG\nUNDOS\nUNDRY\nUNDUE\nUNDUG\nUNDYE\nUNEAT\nUNEND\nUNFAT\nUNFED\nUNFIT\nUNFIX\nUNFOG\nUNFRY\nUNFUN\nUNGAG\nUNGAY\nUNGER\nUNGET\nUNGKA\nUNGOD\nUNGOT\nUNGUM\nUNGUT\nUNHAP\nUNHAT\nUNHEM\nUNHEX\nUNHID\nUNHIP\nUNHIT\nUNHOT\nUNHUG\nUNIAT\nUNIFY\nUNIKE\nUNION\nUNIOS\nUNISI\nUNITE\nUNITS\nUNITY\nUNIWB\nUNIXY\nUNJAM\nUNJAR\nUNJOY\nUNKED\nUNKEL\nUNKEN\nUNKET\nUNKEY\nUNKID\nUNKIE\nUNKLE\nUNLAP\nUNLAW\nUNLAX\nUNLAY\nUNLED\nUNLET\nUNLID\nUNLIT\nUNMAD\nUNMAN\nUNMAP\nUNMET\nUNMEW\nUNMIK\nUNMIX\nUNNUN\nUNODC\nUNODE\nUNOIL\nUNORN\nUNPAY\nUNPEG\nUNPEN\nUNPIN\nUNPOP\nUNPOT\nUNRAR\nUNRED\nUNREP\nUNRID\nUNRIG\nUNRIP\nUNRUG\nUNRUH\nUNRUN\nUNSAW\nUNSAY\nUNSEE\nUNSER\nUNSET\nUNSEW\nUNSEX\nUNSHY\nUNSIN\nUNSLY\nUNSUB\nUNTAG\nUNTAP\nUNTAR\nUNTAX\nUNTIE\nUNTIL\nUNTIN\nUNTYE\nUNUSE\nUNWAD\nUNWEB\nUNWED\nUNWET\nUNWIG\nUNWIN\nUNWIT\nUNWMK\nUNWON\nUNZIP\nUORFS\nUPALI\nUPBAR\nUPBOW\nUPCUT\nUPDIP\nUPDOG\nUPDOS\nUPDRY\nUPEND\nUPENN\nUPFIT\nUPFLY\nUPGMA\nUPGUN\nUPHAM\nUPHER\nUPJET\nUPLAY\nUPLED\nUPLIT\nUPOLU\nUPPAL\nUPPED\nUPPER\nUPPON\nUPRAN\nUPRUN\nUPSEE\nUPSES\nUPSET\nUPSEY\nUPSON\nUPSOT\nUPSUN\nUPTER\nUPTIE\nUPTIL\nUPTON\nUPUPA\nURAEI\nURALI\nURALS\nURARE\nURARI\nURASE\nURATE\nURBAN\nURBES\nURBEX\nURCEI\nURDEE\nUREAL\nUREAS\nUREDO\nUREIC\nUREID\nURENA\nUREYS\nURGED\nURGER\nURGES\nURIAH\nURIAL\nURIAS\nURIBE\nURICH\nURICK\nURIEL\nURIES\nURINE\nURING\nURITE\nURIUS\nURKAS\nURLAR\nURMAN\nURMIA\nURNAL\nURNAS\nURNED\nURNES\nUROPI\nURREA\nURSAL\nURSID\nURSIN\nURSON\nURTIS\nURUBU\nURUMI\nURUPA\nURUTS\nURVAS\nURVED\nURZUA\nUSAAC\nUSAAF\nUSAAS\nUSADA\nUSAFA\nUSAGE\nUSAID\nUSAMA\nUSAMU\nUSBEG\nUSBEK\nUSCGC\nUSDOT\nUSEES\nUSERS\nUSERY\nUSEST\nUSETH\nUSHAS\nUSHED\nUSHER\nUSHES\nUSIAN\nUSIES\nUSING\nUSMAN\nUSMCA\nUSMJP\nUSNEA\nUSNIC\nUSNTS\nUSONA\nUSPIS\nUSPPD\nUSPTO\nUSQUE\nUSREY\nUSRYS\nUSSIE\nUSTAD\nUSTAV\nUSTIC\nUSTOX\nUSUAL\nUSURE\nUSURP\nUSURY\nUTADA\nUTAHN\nUTAHS\nUTCHY\nUTERI\nUTFSE\nUTHER\nUTHES\nUTHLU\nUTIAN\nUTIAS\nUTICA\nUTILE\nUTILS\nUTLEY\nUTSEY\nUTSUL\nUTTER\nUTZED\nUTZES\nUUERS\nUUIDS\nUUISM\nUVALA\nUVATE\nUVCES\nUVEAL\nUVEAN\nUVEAS\nUVITE\nUVULA\nUWAIS\nUYEDA\nUYEZD\nUYGUR\nUZBEG\nUZBEK\nUZSSR\nUZZAH\nUZZLE\nVAASA\nVAAYU\nVACAS\nVACAY\nVACCA\nVACEK\nVACUA\nVADAS\nVADED\nVADEL\nVADEN\nVADES\nVADGE\nVADUZ\nVAGAL\nVAGUE\nVAGUS\nVAHAG\nVAHAN\nVAHLE\nVAILS\nVAIRE\nVAIRS\nVAIRY\nVAJRA\nVAKAS\nVAKIL\nVALEK\nVALES\nVALET\nVALEW\nVALID\nVALIS\nVALKA\nVALKS\nVALLA\nVALOR\nVALPO\nVALPY\nVALSE\nVALUE\nVALVA\nVALVE\nVALVO\nVALYL\nVAMPS\nVAMPY\nVANAS\nVANCE\nVANCO\nVANED\nVANEK\nVANES\nVANGA\nVANGS\nVANIN\nVANIR\nVANJI\nVANNA\nVANNS\nVANTS\nVANYA\nVAPED\nVAPER\nVAPES\nVAPID\nVAPOR\nVARAN\nVARAS\nVARDO\nVARDY\nVAREC\nVARES\nVARGO\nVARIS\nVARIX\nVARNA\nVARNS\nVARON\nVAROS\nVARRY\nVARTS\nVARUN\nVARUS\nVARVE\nVASAL\nVASAS\nVASCO\nVASES\nVASEY\nVASID\nVASKE\nVASKO\nVASSE\nVASTA\nVASTS\nVASTU\nVASTY\nVASUS\nVATES\nVATHS\nVATIC\nVATOS\nVATUS\nVAULT\nVAUNT\nVAUSE\nVAUTS\nVAUTY\nVAVRA\nVAXEN\nVAYES\nVAZES\nVBACS\nVBIED\nVBLOG\nVCRNA\nVCSES\nVEACH\nVEALE\nVEALS\nVEALY\nVECHE\nVEDAS\nVEDDA\nVEDDY\nVEDGE\nVEDIC\nVEDRO\nVEELS\nVEENA\nVEEPS\nVEERS\nVEERY\nVEGAN\nVEGAS\nVEGES\nVEGGO\nVEGIE\nVEGOS\nVEIGA\nVEILS\nVEINS\nVEINY\nVEITH\nVEJAR\nVELAR\nVELCT\nVELDS\nVELDT\nVELES\nVELEZ\nVELIZ\nVELLA\nVELLS\nVELMA\nVELOZ\nVELUM\nVELVA\nVELYE\nVENAL\nVENAS\nVENDA\nVENDS\nVENEW\nVENEY\nVENGE\nVENGO\nVENIN\nVENOM\nVENOS\nVENTA\nVENTI\nVENTO\nVENTS\nVENUE\nVENUS\nVERAS\nVERBS\nVERDE\nVERDI\nVERGE\nVERIE\nVERMA\nVERNA\nVERNE\nVERRE\nVERRY\nVERSE\nVERSO\nVERST\nVERTS\nVERTU\nVERVE\nVERYE\nVESAK\nVESEY\nVESPA\nVESTA\nVESTS\nVETCH\nVETKA\nVETMB\nVETOS\nVETTE\nVEVAY\nVEVES\nVEXED\nVEXER\nVEXES\nVEZIR\nVFATS\nVHEMT\nVHLLS\nVHOST\nVHSES\nVHSIC\nVIALL\nVIALS\nVIANA\nVIAND\nVIARS\nVIARY\nVIAUS\nVIBED\nVIBES\nVIBEX\nVIBEY\nVICAR\nVICED\nVICES\nVICHY\nVICKI\nVICKY\nVICUS\nVIDAR\nVIDAS\nVIDDY\nVIDED\nVIDEO\nVIDES\nVIDIN\nVIDUI\nVIDYA\nVIELE\nVIELS\nVIENS\nVIERS\nVIETH\nVIEWS\nVIEWY\nVIFDA\nVIGAS\nVIGER\nVIGIA\nVIGIL\nVIGNA\nVIGOR\nVIGUE\nVIHAR\nVIJAY\nVIKAS\nVIKES\nVIKKI\nVIKKY\nVILAS\nVILDE\nVILED\nVILER\nVILLA\nVILLI\nVILLS\nVILNA\nVIMBA\nVIMEN\nVIMEO\nVIMPA\nVINAL\nVINAS\nVINAY\nVINCA\nVINCE\nVINED\nVINER\nVINES\nVINEW\nVINEY\nVINHO\nVINIC\nVINKS\nVINNY\nVINTS\nVINYL\nVIOLA\nVIOLS\nVIPER\nVIRAL\nVIRAY\nVIRCH\nVIRED\nVIREO\nVIRES\nVIRGA\nVIRGE\nVIRGO\nVIRID\nVIRII\nVIRJE\nVIRKS\nVIRLS\nVIRON\nVIRSC\nVIRSS\nVIRTU\nVIRUS\nVISAP\nVISAS\nVISCA\nVISCO\nVISCT\nVISED\nVISES\nVISHU\nVISIT\nVISNE\nVISON\nVISOR\nVISTA\nVISTO\nVITAE\nVITAL\nVITAS\nVITEK\nVITES\nVITEX\nVITKI\nVITRE\nVITTA\nVITTI\nVITTS\nVITUG\nVITUS\nVIURA\nVIURE\nVIVAR\nVIVAS\nVIVAT\nVIVDA\nVIVES\nVIVID\nVIXEN\nVIZIR\nVIZOR\nVJING\nVLACH\nVLANS\nVLCCS\nVLCEK\nVLDBS\nVLDLS\nVLEIS\nVLEYS\nVLIES\nVLOCS\nVLOGS\nVMPFC\nVNDER\nVOBLA\nVOCAB\nVOCAL\nVOCIN\nVODAN\nVODDY\nVODER\nVODKA\nVODOU\nVODUN\nVOELS\nVOGAD\nVOGAN\nVOGEL\nVOGGY\nVOGIE\nVOGLE\nVOGLS\nVOGUE\nVOGUL\nVOHRA\nVOICE\nVOICY\nVOIDE\nVOIDS\nVOIGT\nVOILA\nVOILE\nVOITS\nVOJKO\nVOKEN\nVOLAE\nVOLAR\nVOLCY\nVOLDS\nVOLDY\nVOLED\nVOLES\nVOLET\nVOLGA\nVOLGE\nVOLIA\nVOLIN\nVOLKS\nVOLLS\nVOLOF\nVOLOS\nVOLOW\nVOLPE\nVOLPI\nVOLTA\nVOLTE\nVOLTI\nVOLTS\nVOLTZ\nVOLVA\nVOLVE\nVOLVI\nVOLVO\nVOLYN\nVOMER\nVOMIC\nVOMIT\nVOMMA\nVONAS\nVONCE\nVONDA\nVONGS\nVOOKS\nVOONG\nVOPOS\nVORAS\nVORED\nVORES\nVOROS\nVOSES\nVOSIP\nVOTAW\nVOTED\nVOTER\nVOTES\nVOTHS\nVOTIC\nVOUCH\nVOUGE\nVOULA\nVOULD\nVOWED\nVOWEL\nVOWER\nVOWES\nVOXEL\nVOXES\nVOYCE\nVOYDE\nVOYDS\nVOYOL\nVOZHD\nVPPON\nVPSOS\nVRAIC\nVRAKA\nVRANA\nVRBAS\nVRBJE\nVRNAS\nVROCK\nVROOM\nVROUW\nVROWS\nVRSAR\nVRTRA\nVSATS\nVSEPR\nVSICS\nVSOPS\nVSPAN\nVTBLS\nVUGGS\nVUGGY\nVUGHS\nVUGHY\nVUKAC\nVULGO\nVULNS\nVULVA\nVUNJO\nVUONG\nVURPS\nVUSAS\nVWORP\nVXERS\nVYASA\nVYGIE\nVYING\nVYSAR\nVYTIN\nWAAAY\nWAAGS\nWAAMA\nWAAWS\nWACKE\nWACKO\nWACKS\nWACKY\nWADDS\nWADDY\nWADED\nWADER\nWADES\nWADGE\nWADIS\nWAEGS\nWAFER\nWAFFS\nWAFTS\nWAFTY\nWAGAH\nWAGAR\nWAGED\nWAGEL\nWAGER\nWAGES\nWAGGA\nWAGGE\nWAGGY\nWAGLE\nWAGON\nWAGYU\nWAHAB\nWAHBA\nWAHEY\nWAHID\nWAHOO\nWAIDE\nWAIDS\nWAIES\nWAIFS\nWAIFT\nWAIFU\nWAIFY\nWAILE\nWAILS\nWAINS\nWAINT\nWAIRS\nWAIST\nWAITE\nWAITS\nWAIVE\nWAJDA\nWAJIB\nWAKAS\nWAKAW\nWAKED\nWAKEN\nWAKER\nWAKES\nWAKEY\nWAKFS\nWAKIF\nWAKIL\nWALCH\nWALCK\nWALDO\nWALDS\nWALED\nWALER\nWALES\nWALIS\nWALKE\nWALKO\nWALKS\nWALKT\nWALLA\nWALLE\nWALLS\nWALLY\nWALMS\nWALPS\nWALRI\nWALSH\nWALTY\nWALTZ\nWAMES\nWAMPS\nWAMUS\nWANDA\nWANDE\nWANDS\nWANDY\nWANED\nWANEK\nWANES\nWANEY\nWANGA\nWANGO\nWANGS\nWANJI\nWANKA\nWANKE\nWANKS\nWANKY\nWANLI\nWANLY\nWANNA\nWANSE\nWANST\nWANTA\nWANTE\nWANTS\nWANTY\nWANZE\nWAPED\nWAPPS\nWAQFS\nWAQIF\nWARAL\nWARAY\nWARDA\nWARDS\nWARED\nWARES\nWAREZ\nWARFS\nWARGI\nWARGO\nWARGS\nWARIA\nWARKS\nWARLI\nWARLY\nWARMS\nWARMY\nWARNE\nWARNO\nWARNS\nWARPS\nWARRA\nWARRE\nWARRS\nWARRU\nWARRY\nWARSH\nWARTA\nWARTH\nWARTS\nWARTY\nWASCO\nWASES\nWASHI\nWASHT\nWASHY\nWASIK\nWASKO\nWASLA\nWASMS\nWASNA\nWASNT\nWASON\nWASPS\nWASPY\nWASSY\nWASTA\nWASTE\nWASTS\nWASTY\nWASUP\nWATAP\nWATCH\nWATER\nWATHA\nWATHE\nWATHS\nWATRY\nWATSU\nWATTS\nWAUGH\nWAUJA\nWAUKS\nWAULK\nWAULS\nWAVED\nWAVER\nWAVES\nWAVEY\nWAWED\nWAWES\nWAWLS\nWAXED\nWAXEN\nWAXER\nWAXES\nWAXIE\nWAYED\nWAYES\nWAYNE\nWAYNS\nWAYTS\nWAYUU\nWAZEE\nWAZIR\nWAZOO\nWAZUP\nWAZZA\nWBAFC\nWCDMA\nWDTHS\nWDYFW\nWEAKE\nWEAKY\nWEALD\nWEALE\nWEALS\nWEALY\nWEANS\nWEARE\nWEARS\nWEARY\nWEASE\nWEAST\nWEASY\nWEAVE\nWEAVY\nWEBBY\nWEBER\nWEBRE\nWECHT\nWECKS\nWEDGE\nWEDGY\nWEEBS\nWEEDS\nWEEDY\nWEEKE\nWEEKS\nWEELE\nWEELS\nWEELY\nWEENS\nWEENY\nWEEPE\nWEEPS\nWEEPY\nWEERO\nWEESE\nWEEST\nWEETS\nWEFIE\nWEFTS\nWEGER\nWEICK\nWEIER\nWEIGH\nWEILS\nWEIRD\nWEIRS\nWEISM\nWEISS\nWEIST\nWEISZ\nWEITZ\nWEIVE\nWEKAS\nWEKAU\nWELAN\nWELCH\nWELCS\nWELDS\nWELDY\nWELKE\nWELKS\nWELLS\nWELLY\nWELPS\nWELSH\nWELTE\nWELTS\nWELTY\nWENCH\nWENDI\nWENDS\nWENDT\nWENDY\nWENGE\nWENIS\nWENKS\nWENNY\nWENTE\nWENTS\nWENTZ\nWEPTE\nWEREN\nWERES\nWERKE\nWERKS\nWERLE\nWERNS\nWERRE\nWERRY\nWERSH\nWERST\nWERTH\nWERTZ\nWESER\nWESIL\nWESTS\nWESTY\nWETAS\nWETLY\nWETUS\nWEUNS\nWEVER\nWEVIL\nWEXED\nWEXES\nWEXIS\nWEYER\nWEYES\nWEYVE\nWEZON\nWFSPS\nWGARA\nWGWAG\nWHAAP\nWHACK\nWHALE\nWHALL\nWHAME\nWHAMO\nWHAMS\nWHANG\nWHANK\nWHAPS\nWHARE\nWHARF\nWHARL\nWHARP\nWHATH\nWHATS\nWHAUL\nWHAUP\nWHEAL\nWHEAT\nWHEEK\nWHEEL\nWHEEN\nWHEFT\nWHEKI\nWHELK\nWHELM\nWHELP\nWHENS\nWHEPT\nWHERE\nWHETS\nWHEWS\nWHEYN\nWHEYS\nWHICH\nWHIDS\nWHIES\nWHIFF\nWHIGS\nWHILE\nWHILK\nWHIMP\nWHIMS\nWHINE\nWHING\nWHINS\nWHINY\nWHIOS\nWHIPP\nWHIPS\nWHIPT\nWHIRL\nWHIRR\nWHIRS\nWHISH\nWHISK\nWHISP\nWHIST\nWHITE\nWHITH\nWHITS\nWHITT\nWHITY\nWHIZZ\nWHMIS\nWHOAH\nWHOAM\nWHOAS\nWHOES\nWHOIS\nWHOLE\nWHOLY\nWHOME\nWHOMP\nWHOOF\nWHOOP\nWHOOS\nWHOOT\nWHOPS\nWHORE\nWHORL\nWHORT\nWHOSE\nWHOSO\nWHOZE\nWHRRR\nWHUMP\nWHUPS\nWHURS\nWHURT\nWHUSS\nWHYDA\nWHYJA\nWHYLE\nWHYOS\nWHYTE\nWIANT\nWIBLE\nWIBNI\nWICCA\nWICHI\nWICHU\nWICKE\nWICKS\nWICKY\nWIDDY\nWIDEN\nWIDER\nWIDES\nWIDGE\nWIDIA\nWIDOW\nWIDTH\nWIEBE\nWIECK\nWIELD\nWIENS\nWIERD\nWIERS\nWIERY\nWIEST\nWIFES\nWIFEY\nWIFIE\nWIFIS\nWIFTY\nWIGAN\nWIGGA\nWIGGO\nWIGGS\nWIGGY\nWIGHT\nWIKES\nWIKIA\nWIKIS\nWILBE\nWILBY\nWILCO\nWILDE\nWILDS\nWILDT\nWILED\nWILES\nWILEY\nWILGA\nWILJA\nWILKE\nWILKS\nWILLA\nWILLE\nWILLS\nWILLY\nWILMA\nWILMS\nWILNA\nWILTS\nWILTY\nWILTZ\nWIMAX\nWIMBO\nWIMEN\nWIMER\nWIMEX\nWIMIN\nWIMPS\nWIMPY\nWINAL\nWINCE\nWINCH\nWINDA\nWINDE\nWINDS\nWINDY\nWINED\nWINER\nWINES\nWINEY\nWINGE\nWINGO\nWINGS\nWINGY\nWINJS\nWINKS\nWINKT\nWINKY\nWINLY\nWINNE\nWINNI\nWINOS\nWINPE\nWINRT\nWINTS\nWINTU\nWINTZ\nWINXP\nWINZE\nWIPED\nWIPER\nWIPES\nWIPFS\nWIRED\nWIRER\nWIRES\nWIRRA\nWIRTH\nWIRTZ\nWISED\nWISEN\nWISER\nWISES\nWISHA\nWISHE\nWISHT\nWISLY\nWISOR\nWISPS\nWISPY\nWISSE\nWISTS\nWITAN\nWITCH\nWITED\nWITEK\nWITES\nWITHE\nWITHS\nWITHY\nWITTE\nWITTS\nWITTY\nWIVED\nWIVER\nWIVES\nWIWAL\nWIXES\nWIXOM\nWIXON\nWIYOT\nWIZEN\nWKBKS\nWKEND\nWKIDS\nWKNDS\nWKSHT\nWKSTS\nWLANS\nWLRGS\nWMAFS\nWMATA\nWNEKS\nWOADS\nWOADY\nWOALD\nWOANT\nWOBLA\nWOCAS\nWOCUS\nWODEN\nWODGE\nWOFUL\nWOIDS\nWOJUS\nWOKAS\nWOKED\nWOKEN\nWOKOU\nWOKUS\nWOLAK\nWOLDE\nWOLDS\nWOLFE\nWOLFS\nWOLFY\nWOLIN\nWOLKS\nWOLLY\nWOLOF\nWOLVE\nWOMAN\nWOMBS\nWOMBY\nWOMEN\nWOMER\nWOMIN\nWOMON\nWOMXN\nWOMYN\nWONCE\nWONED\nWONES\nWONGA\nWONGI\nWONGS\nWONKS\nWONKY\nWONNE\nWONTS\nWONUT\nWOODE\nWOODS\nWOODY\nWOOED\nWOOER\nWOOES\nWOOFS\nWOOFY\nWOOLD\nWOOLF\nWOOLS\nWOOLY\nWOOPS\nWOOSH\nWOOSY\nWOOTZ\nWOOZE\nWOOZY\nWORAS\nWORCS\nWORDE\nWORDS\nWORDY\nWORES\nWORKE\nWORKS\nWORKU\nWORKY\nWORLD\nWORME\nWORMS\nWORMY\nWORNE\nWORRA\nWORRY\nWORSE\nWORST\nWORTH\nWORTS\nWORTY\nWOSBS\nWOUGH\nWOULD\nWOULS\nWOUND\nWOVEN\nWOWED\nWOWEE\nWOWIE\nWOWKE\nWOWND\nWOWZA\nWOXEN\nWOZNY\nWPANS\nWPBSA\nWPGMA\nWRACK\nWRAKE\nWRAMP\nWRANG\nWRAPS\nWRAPT\nWRATH\nWRATS\nWRAWL\nWRAYS\nWREAK\nWRECK\nWREDE\nWRENN\nWRENS\nWREST\nWREYE\nWRICK\nWRIDE\nWRIED\nWRIER\nWRIES\nWRIGS\nWRILY\nWRINE\nWRING\nWRIST\nWRITE\nWRITH\nWRITO\nWRITS\nWROCK\nWROKE\nWRONA\nWRONG\nWROOS\nWROTE\nWROTH\nWRPGS\nWRUNG\nWRYER\nWRYLY\nWSDLS\nWSDOT\nWSOPS\nWSSLS\nWTFPL\nWUDHU\nWUDUP\nWUEST\nWUFFO\nWUFFS\nWUHAN\nWUHSI\nWUISM\nWULFF\nWULST\nWUNCH\nWUNGA\nWUNGU\nWUNNA\nWUNST\nWURLY\nWURST\nWURTH\nWURTZ\nWUSHU\nWUSSY\nWUSTL\nWUSUN\nWUVED\nWUXGA\nWUXIA\nWUXUE\nWUZHI\nWUZZA\nWUZZY\nWWIII\nWWOOF\nWWTPS\nWWWWW\nWYANT\nWYATT\nWYBLE\nWYCHE\nWYDAH\nWYDER\nWYDES\nWYERS\nWYLER\nWYLIE\nWYMAN\nWYMER\nWYMYN\nWYNDS\nWYNES\nWYNGE\nWYNGZ\nWYNIA\nWYNNE\nWYNNS\nWYPES\nWYRDS\nWYRES\nWYRMS\nWYSES\nWYTCH\nWYTED\nWYTES\nWYTHE\nWYVES\nXACML\nXALAM\nXALWO\nXAMIR\nXANAX\nXANNY\nXANOL\nXASER\nXBOWS\nXEBEC\nXEGWI\nXEMES\nXENAS\nXENIA\nXENIC\nXENID\nXENON\nXENYL\nXERIC\nXERIF\nXEROX\nXFELS\nXFERS\nXHOSA\nXHTML\nXIALU\nXIANG\nXIANS\nXIAOS\nXINCA\nXINGS\nXINGU\nXIONG\nXIQIN\nXLINK\nXNAND\nXOANA\nXOLOS\nXORED\nXORNS\nXOSAS\nXPATH\nXPCOM\nXPERS\nXPOST\nXQUIC\nXRAYS\nXSRFS\nXTALS\nXTIAN\nXTMAS\nXUETA\nXUIXO\nXUXOS\nXXXED\nXXXES\nXYLAN\nXYLEM\nXYLIC\nXYLOL\nXYLOS\nXYLYL\nXYRID\nXYRIS\nXYSMA\nXYSTA\nXYSTI\nXYSTS\nXYZZY\nYAAAS\nYAAAY\nYAAKU\nYAARS\nYABBY\nYABIM\nYABOO\nYABUT\nYACCA\nYACHT\nYACKS\nYACON\nYADAO\nYADAV\nYADDA\nYADIM\nYADON\nYADUS\nYAEKO\nYAFFE\nYAGAR\nYAGER\nYAGIS\nYAGNA\nYAGNI\nYAGYA\nYAHIR\nYAHNS\nYAHOO\nYAHVE\nYAHWE\nYAHYA\nYAIRD\nYAJNA\nYAKAL\nYAKIN\nYAKKA\nYAKKY\nYAKOW\nYAKUT\nYALAH\nYALDA\nYALES\nYALIE\nYALIS\nYALLO\nYALTA\nYAMAL\nYAMEN\nYAMMA\nYAMMY\nYAMPA\nYAMPI\nYAMPY\nYAMUN\nYANAS\nYANBU\nYANCE\nYANCY\nYANDY\nYANEZ\nYANGS\nYANKE\nYANKS\nYANNI\nYANNO\nYANOS\nYANTS\nYANZI\nYAPAN\nYAPLE\nYAPOK\nYAPON\nYAPPS\nYAPPY\nYARAK\nYARAM\nYARBS\nYARCO\nYARDS\nYAREN\nYARER\nYARFA\nYARKE\nYARKS\nYARLS\nYARMS\nYARNE\nYARNS\nYARNY\nYAROO\nYARRI\nYARRS\nYARTA\nYARUM\nYASAK\nYASDS\nYASHT\nYASNA\nYASSA\nYASSY\nYASUO\nYATAI\nYATCH\nYATES\nYATGA\nYATHI\nYATRA\nYATRI\nYATTA\nYATTS\nYATZY\nYAUDS\nYAULD\nYAULS\nYAUPS\nYAVIS\nYAVNE\nYAWED\nYAWEY\nYAWLS\nYAWNS\nYAWNY\nYAWPS\nYAYAS\nYAYOI\nYAYUE\nYAYUH\nYAZHS\nYAZOO\nYBEEN\nYBENT\nYBORE\nYBORN\nYCLAD\nYCOND\nYDDIM\nYEADS\nYEALM\nYEANS\nYEARA\nYEARD\nYEARE\nYEARN\nYEARS\nYEARY\nYEAST\nYEATS\nYEBRA\nYECCH\nYECHS\nYECHY\nYEDDO\nYEECH\nYEEES\nYEEHA\nYEELD\nYEENS\nYEEPS\nYEERE\nYEERS\nYEESH\nYEETS\nYEEZY\nYEGGS\nYEILD\nYEKES\nYEKKE\nYELEK\nYELKS\nYELLA\nYELLE\nYELLO\nYELLS\nYELMS\nYELOW\nYELPS\nYELPT\nYELPY\nYELVE\nYEMAN\nYEMEN\nYENNA\nYENOM\nYENTA\nYENTE\nYEPEZ\nYEPLY\nYEPPO\nYERBA\nYERBS\nYERBY\nYERKS\nYERNS\nYERON\nYEROS\nYERTH\nYESES\nYESKS\nYESSO\nYESSS\nYESTS\nYESTY\nYETIS\nYETTS\nYEUCH\nYEUGH\nYEUKS\nYEUKY\nYEUNG\nYEWEI\nYEWEN\nYEXED\nYEXES\nYEYSK\nYEZDI\nYFERE\nYGONE\nYHJBT\nYIBIN\nYIDAM\nYIDDO\nYIELD\nYIFFS\nYIFFY\nYIISH\nYIKES\nYILAN\nYILLS\nYIMBY\nYINGS\nYIPEE\nYIPES\nYIPPY\nYIRKS\nYITES\nYIVEN\nYIVES\nYKWIM\nYLAID\nYLAND\nYLEFT\nYLIDE\nYLIDS\nYLIKE\nYLOND\nYMADE\nYMCAS\nYMIXT\nYMOLT\nYMPES\nYNOLS\nYOABU\nYOAKE\nYOAKS\nYOBBO\nYOBOS\nYOCKS\nYOCOM\nYOCUM\nYODEL\nYODER\nYODHS\nYODLE\nYOGAS\nYOGEE\nYOGHS\nYOGIC\nYOGIN\nYOGIS\nYOHES\nYOHNS\nYOHOS\nYOICK\nYOIKS\nYOINK\nYOISM\nYOITS\nYOJAN\nYOKAI\nYOKAN\nYOKED\nYOKEL\nYOKER\nYOKES\nYOKUL\nYOKUM\nYOLES\nYOLKS\nYOLKY\nYOLLA\nYOMPS\nYONAN\nYONCE\nYONGE\nYONGS\nYONIC\nYONIS\nYONKS\nYONNE\nYONTZ\nYOOFS\nYOONG\nYOONS\nYOOPS\nYOPON\nYOPPS\nYORDY\nYORES\nYORGA\nYORKE\nYORKS\nYORON\nYOSEF\nYOSHI\nYOTED\nYOTES\nYOUCH\nYOUED\nYOUEE\nYOUKS\nYOUNG\nYOUNS\nYOUNT\nYOURE\nYOURN\nYOURS\nYOURT\nYOUSE\nYOUTH\nYOUTS\nYOUZE\nYOWCH\nYOWES\nYOWIE\nYOWLS\nYOWLY\nYOWZA\nYOYOS\nYPAID\nYPIPO\nYPRES\nYRAFT\nYRAPT\nYRARE\nYRAST\nYREKA\nYRENT\nYRIVD\nYRNEH\nYTHAN\nYTHES\nYTOLD\nYTOST\nYUANS\nYUCAS\nYUCCA\nYUCCH\nYUCHI\nYUCKS\nYUCKY\nYUENS\nYUFTS\nYUGAS\nYUGEN\nYUGUR\nYUIKO\nYUINA\nYUKED\nYUKES\nYUKIE\nYUKIO\nYUKKY\nYUKON\nYUKOS\nYULAN\nYULES\nYUMAN\nYUMAS\nYUMIS\nYUMMO\nYUMMY\nYUMPS\nYUNFU\nYUNGS\nYUNNO\nYUNXI\nYUORY\nYUPIK\nYUPON\nYUPPO\nYUPPY\nYURES\nYURIE\nYURKO\nYURLA\nYUROK\nYUROP\nYURTA\nYURTS\nYURUK\nYUSES\nYUSHO\nYUSKO\nYUTAN\nYUTZY\nYUZUS\nYVORY\nYWAIN\nYWCAS\nYWENT\nZABAD\nZABKA\nZABOK\nZABRA\nZACKS\nZADAK\nZADAR\nZADOK\nZADOQ\nZAFAR\nZAFRA\nZAFUS\nZAGAL\nZAGAR\nZAGER\nZAHID\nZAHIR\nZAHLE\nZAHMS\nZAHRA\nZAIDI\nZAILS\nZAIMS\nZAINO\nZAINS\nZAIRE\nZAISU\nZAIWA\nZAJAC\nZAJAL\nZAKAH\nZAKAT\nZAKIS\nZAMAK\nZAMAN\nZAMAS\nZAMBO\nZAMIA\nZAMIS\nZAMOR\nZANDE\nZANGI\nZANGS\nZANJA\nZANJE\nZANJS\nZANTE\nZANZA\nZANZE\nZANZY\nZAPAS\nZAPFS\nZAPPA\nZAPPY\nZARBI\nZARCO\nZARDA\nZARFS\nZARIA\nZARIF\nZARMA\nZARPH\nZARPS\nZATIS\nZATOR\nZAUGG\nZAWNS\nZAXES\nZAYAT\nZAYDE\nZAYDI\nZAYED\nZAYIN\nZAYNE\nZAYNS\nZAZAO\nZAZAS\nZAZEN\nZAZZY\nZDDPS\nZEALE\nZEALS\nZEBAK\nZEBEC\nZEBRA\nZEBUB\nZEBUL\nZEBUS\nZEEKS\nZEERA\nZEERS\nZEHRS\nZEIDS\nZEIDY\nZEINS\nZEISS\nZEITZ\nZELDA\nZELIG\nZELMA\nZELUS\nZEMIS\nZEMKE\nZEMNI\nZENDE\nZENDO\nZENER\nZENGI\nZENGS\nZENIK\nZENKS\nZENON\nZENTS\nZENTZ\nZEOLI\nZEPPS\nZERBE\nZERBY\nZERDA\nZERDE\nZEREN\nZERGS\nZERKS\nZEROA\nZEROS\nZERRS\nZESTS\nZESTY\nZETAS\nZEZES\nZHABA\nZHAIS\nZHANG\nZHANS\nZHAPU\nZHEES\nZHENG\nZHENS\nZHLUB\nZHOMO\nZHONG\nZHUOS\nZHUSH\nZHUXI\nZHUZH\nZIBAR\nZIBET\nZICKS\nZIEBA\nZIEGA\nZIEGE\nZIFTS\nZIGGY\nZIGUA\nZIGUI\nZIIPS\nZIKAT\nZIKRI\nZIKRS\nZILAS\nZILCH\nZILDE\nZILKA\nZILLS\nZIMAS\nZIMBA\nZIMBI\nZIMBO\nZIMBS\nZINAB\nZINCK\nZINCO\nZINCS\nZINCY\nZINDA\nZINEB\nZINER\nZINES\nZINGS\nZINGY\nZINKE\nZINKS\nZINKY\nZINOS\nZINZA\nZIONS\nZIPPO\nZIPPS\nZIPPY\nZIRAM\nZIRPS\nZITAN\nZITIS\nZITSO\nZITTY\nZIVES\nZIZEL\nZIZIT\nZIZZO\nZIZZY\nZLOTE\nZLOTY\nZMINJ\nZMUDA\nZOAEA\nZOARS\nZOBOS\nZOCCO\nZOCKS\nZOCLE\nZOEAE\nZOEAL\nZOEAS\nZOHAR\nZOIDS\nZOIGL\nZOIKS\nZOILI\nZOISM\nZOIST\nZOITE\nZOKOR\nZOLLO\nZOLLY\nZOMBI\nZOMBS\nZOMES\nZOMFG\nZOMIA\nZOMMA\nZONAE\nZONAL\nZONAS\nZONDA\nZONED\nZONER\nZONES\nZONKS\nZONKY\nZOOEA\nZOOEY\nZOOID\nZOOKS\nZOOMS\nZOOMY\nZOONS\nZOOTS\nZOOTY\nZOPAS\nZOPPO\nZOQUE\nZORBS\nZORCH\nZORID\nZORIL\nZORIS\nZORRO\nZORSE\nZOSHI\nZOUKS\nZOWEE\nZOWIE\nZOWLS\nZRAZY\nZUBER\nZUBIA\nZUBRS\nZUCCO\nZUCHE\nZUISM\nZUIST\nZUKES\nZULES\nZULLO\nZULUS\nZUMAT\nZUMBA\nZUMBI\nZUMMO\nZUNIS\nZUNOS\nZUPAN\nZUPAS\nZUPPA\nZUPPE\nZUREK\nZURFS\nZURLA\nZURMA\nZURNA\nZURNS\nZUZES\nZUZIM\nZWART\nZWICK\nZWNJS\nZYGAL\nZYGON\nZYMAD\nZYMES\nZYMIC\nZYMIN\nZYXIN\nZZYZX\nZZZED"
  },
  {
    "path": "core/src/main/res/raw/wordle_list_es.txt",
    "content": "ABABA\nABACA\nABACO\nABADA\nABADI\nABAJA\nABAJE\nABAJO\nABALA\nABALE\nABALO\nABANA\nABANE\nABANO\nABASI\nABATA\nABATE\nABATI\nABATO\nABECE\nABEJA\nABETE\nABETO\nABIAR\nABIAS\nABINA\nABINE\nABINO\nABISO\nABITA\nABITE\nABITO\nABOBA\nABOBE\nABOBO\nABOCA\nABOCO\nABOFA\nABOFE\nABOFO\nABOGA\nABOGO\nABOLI\nABONA\nABONE\nABONO\nABOYA\nABOYE\nABOYO\nABOZO\nABRAN\nABRAS\nABREN\nABRES\nABRIA\nABRID\nABRIL\nABRIO\nABRIR\nABRIS\nABSIT\nABUBO\nABUCE\nABUJE\nABURA\nABURE\nABURO\nABUSA\nABUSE\nABUSO\nABUZA\nABUZO\nACABA\nACABE\nACABO\nACAMA\nACAME\nACAMO\nACANA\nACARA\nACARE\nACARO\nACASO\nACATA\nACATE\nACATO\nACEBO\nACECE\nACEDA\nACEDE\nACEDO\nACEMA\nACEPA\nACEPE\nACEPO\nACERA\nACERE\nACERO\nACETA\nACETO\nACEZA\nACEZO\nACENA\nACHIN\nACHIS\nACIAL\nACIAR\nACIDA\nACIDO\nACIJE\nACILO\nACIMO\nACION\nACLES\nACLLA\nACMES\nACNES\nACOCA\nACOCO\nACODA\nACODE\nACODO\nACOGE\nACOGI\nACOJA\nACOJO\nACOLA\nACOLE\nACOLO\nACOPA\nACOPE\nACOPO\nACORA\nACORE\nACORO\nACOSA\nACOSE\nACOSO\nACOTA\nACOTE\nACOTO\nACRES\nACROE\nACROY\nACTAS\nACTEA\nACTOR\nACTOS\nACTUA\nACTUE\nACTUO\nACUDA\nACUDE\nACUDI\nACUDO\nACUEA\nACUEO\nACULA\nACULE\nACULO\nACUNA\nACUNE\nACUNO\nACURE\nACUSA\nACUSE\nACUSO\nACUTA\nACUTI\nACUTO\nACUYO\nADALA\nADAMA\nADAME\nADAMO\nADAZA\nADEMA\nADEME\nADEMO\nADIAD\nADIAN\nADIAR\nADIAS\nADIEN\nADIES\nADIOS\nADIVA\nADIVE\nADOBA\nADOBE\nADOBO\nADORA\nADORE\nADORO\nADOSA\nADOSE\nADOSO\nADRAD\nADRAL\nADRAN\nADRAR\nADRAS\nADREN\nADRES\nADUAR\nADUCE\nADUCI\nADUFE\nADUJA\nADUJE\nADUJO\nADULA\nADULE\nADULO\nADUNA\nADUNE\nADUNO\nADURA\nADURE\nADURI\nADURO\nADVEN\nAEDAS\nAEDOS\nAEREA\nAEREO\nAETAS\nAFACA\nAFACE\nAFAGA\nAFAGO\nAFAMA\nAFAME\nAFAMO\nAFANA\nAFANE\nAFANO\nAFARA\nAFARE\nAFATA\nAFATE\nAFATO\nAFEAD\nAFEAN\nAFEAR\nAFEAS\nAFEEN\nAFEES\nAFIAR\nAFICE\nAFIJA\nAFIJO\nAFILA\nAFILE\nAFILO\nAFINA\nAFINE\nAFINO\nAFIZO\nAFLUI\nAFOCA\nAFOCO\nAFOFA\nAFOFE\nAFOFO\nAFOGA\nAFOGO\nAFONA\nAFONO\nAFORA\nAFORE\nAFORO\nAFOSA\nAFOSE\nAFOSO\nAFTAS\nAFUFA\nAFUFE\nAFUFO\nAFUMA\nAFUME\nAFUMO\nAGACE\nAGAMI\nAGANA\nAGANE\nAGANO\nAGAPE\nAGATA\nAGAVE\nAGIOS\nAGITA\nAGITE\nAGITO\nAGNUS\nAGOLA\nAGOLE\nAGOLO\nAGORA\nAGORE\nAGORO\nAGOTA\nAGOTE\nAGOTO\nAGRAS\nAGRAZ\nAGRES\nAGRIA\nAGRIE\nAGRIO\nAGROR\nAGROS\nAGUAD\nAGUAI\nAGUAN\nAGUAR\nAGUAS\nAGUAY\nAGUCE\nAGUDA\nAGUDO\nAGUEN\nAGUES\nAGUIN\nAGUIO\nAGUJA\nAGUTI\nAGUZA\nAGUZO\nAHAJA\nAHAJE\nAHAJO\nAHIJA\nAHIJE\nAHIJO\nAHILA\nAHILE\nAHILO\nAHINA\nAHITA\nAHITE\nAHITO\nAHOGA\nAHOGO\nAHORA\nAHOYA\nAHOYE\nAHOYO\nAHUMA\nAHUME\nAHUMO\nAHUSA\nAHUSE\nAHUSO\nAILLO\nAILLU\nAINAS\nAIRAD\nAIRAN\nAIRAR\nAIRAS\nAIREA\nAIREE\nAIREN\nAIREO\nAIRES\nAIRON\nAISAS\nAISLA\nAISLE\nAISLO\nAITES\nAJABA\nAJADA\nAJADO\nAJAIS\nAJAJA\nAJARA\nAJARE\nAJASE\nAJEAD\nAJEAN\nAJEAR\nAJEAS\nAJEBE\nAJEEN\nAJEES\nAJEIS\nAJENA\nAJENO\nAJEOS\nAJERA\nAJERO\nAJETE\nAJICE\nAJIES\nAJIPA\nAJIZA\nAJIZO\nAJOBO\nAJORA\nAJORE\nAJORO\nAJOTA\nAJOTE\nAJOTO\nAJUAR\nAJUMA\nAJUME\nAJUMO\nAJUNA\nAJUNO\nALABA\nALABE\nALABO\nALACO\nALADA\nALADO\nALAFA\nALAGA\nALAGO\nALAJU\nALALA\nALALO\nALAMA\nALAMO\nALANA\nALANO\nALAUI\nALAZO\nALBAR\nALBAS\nALBEA\nALBEE\nALBEO\nALBIN\nALBOR\nALBOS\nALBUM\nALBUR\nALCAS\nALCEA\nALCEN\nALCES\nALCOR\nALDEA\nALEAD\nALEAN\nALEAR\nALEAS\nALECE\nALEDA\nALEEN\nALEES\nALEFS\nALEGA\nALEGO\nALEJA\nALEJE\nALEJO\nALELA\nALELE\nALELI\nALELO\nALEMA\nALERO\nALETA\nALETO\nALEVE\nALEYA\nALEZO\nALFAD\nALFAN\nALFAR\nALFAS\nALFEN\nALFES\nALFIL\nALFIZ\nALFOZ\nALGAR\nALGAS\nALGOL\nALGOS\nALGUN\nALHOZ\nALIAD\nALIAN\nALIAR\nALIAS\nALICA\nALIEN\nALIER\nALIES\nALIFA\nALIGA\nALIGO\nALIJA\nALIJE\nALIJO\nALIMO\nALIMS\nALIOJ\nALISA\nALISE\nALISO\nALINA\nALINE\nALINO\nALJEZ\nALJOR\nALLEN\nALMAS\nALMEA\nALMEZ\nALMOS\nALMUD\nALNAS\nALNOS\nALOBA\nALOBE\nALOBO\nALOCA\nALOCO\nALOES\nALOJA\nALOJE\nALOJO\nALOLA\nALOLE\nALOLO\nALOMA\nALOME\nALOMO\nALONA\nALORA\nALOSA\nALOTA\nALOTE\nALOTO\nALOYA\nALPES\nALTAR\nALTAS\nALTEA\nALTEE\nALTEO\nALTOR\nALTOS\nALUAS\nALUCE\nALUDA\nALUDE\nALUDI\nALUDO\nALULA\nALUNA\nALUNE\nALUNO\nALUZA\nALUZO\nALVEO\nALZAD\nALZAN\nALZAR\nALZAS\nALZOS\nAMABA\nAMADA\nAMADO\nAMAGA\nAMAGO\nAMAIS\nAMALA\nAMALE\nAMALO\nAMANA\nAMANE\nAMANO\nAMARA\nAMARE\nAMARO\nAMASA\nAMASE\nAMASO\nAMATA\nAMATE\nAMATO\nAMBAR\nAMBAS\nAMBLA\nAMBLE\nAMBLO\nAMBON\nAMBOS\nAMEBA\nAMEIS\nAMELA\nAMELE\nAMELO\nAMENA\nAMENO\nAMEOS\nAMERA\nAMERE\nAMERO\nAMIAS\nAMIBA\nAMIBO\nAMIDA\nAMIGA\nAMIGO\nAMINA\nAMINE\nAMINO\nAMIRI\nAMITO\nAMOLA\nAMOLE\nAMOLO\nAMOMO\nAMONA\nAMONE\nAMONO\nAMOVE\nAMOVI\nAMPAY\nAMPLA\nAMPLO\nAMPON\nAMPOS\nAMPRA\nAMPRE\nAMPRO\nAMUGA\nAMUGO\nAMULA\nAMULE\nAMULO\nAMURA\nAMURE\nAMURO\nAMUSO\nANABI\nANACO\nANADE\nANAFE\nANAMU\nANANA\nANATA\nANCAS\nANCHA\nANCHE\nANCHO\nANCLA\nANCLE\nANCLO\nANCON\nANCUA\nANDAD\nANDAN\nANDAR\nANDAS\nANDEL\nANDEN\nANDES\nANDON\nANEAD\nANEAN\nANEAR\nANEAS\nANEEN\nANEES\nANEGA\nANEGO\nANEJA\nANEJE\nANEJO\nANETO\nANEXA\nANEXE\nANEXO\nANGEL\nANGLA\nANGLO\nANGOR\nANGRA\nANGUS\nANIDA\nANIDE\nANIDO\nANIMA\nANIME\nANIMO\nANION\nANISA\nANISE\nANISO\nANITO\nANINA\nANINE\nANINO\nANJEO\nANOAS\nANODO\nANOLA\nANOLE\nANOLO\nANONA\nANOTA\nANOTE\nANOTO\nANSAR\nANSAS\nANSIA\nANSIE\nANSIO\nANTAS\nANTES\nANTIA\nANTIS\nANTRO\nANUAL\nANUAS\nANUDA\nANUDE\nANUDO\nANULA\nANULE\nANULO\nANUOS\nANURA\nANURO\nAOCAR\nAOJAD\nAOJAN\nAOJAR\nAOJAS\nAOJEN\nAOJES\nAOJOS\nAONIA\nAONIO\nAORTA\nAOVAD\nAOVAN\nAOVAR\nAOVAS\nAOVEN\nAOVES\nAPAGA\nAPAGO\nAPALE\nAPARA\nAPARE\nAPARO\nAPANA\nAPANE\nAPANO\nAPEAD\nAPEAN\nAPEAR\nAPEAS\nAPEEN\nAPEES\nAPEGA\nAPEGO\nAPELA\nAPELE\nAPELO\nAPENA\nAPENE\nAPENO\nAPEOS\nAPERA\nAPERE\nAPERO\nAPESE\nAPICE\nAPILA\nAPILE\nAPILO\nAPIOS\nAPIPA\nAPIPE\nAPIPO\nAPIRI\nAPITA\nAPITE\nAPITO\nAPINA\nAPINE\nAPINO\nAPNEA\nAPOCA\nAPOCE\nAPOCO\nAPODA\nAPODE\nAPODO\nAPOLA\nAPOLE\nAPOLO\nAPONE\nAPOSA\nAPOSE\nAPOSO\nAPOYA\nAPOYE\nAPOYO\nAPOZA\nAPOZO\nAPRES\nAPROA\nAPROE\nAPROO\nAPTAR\nAPTAS\nAPTOS\nAPUNA\nAPUNE\nAPUNO\nAPURA\nAPURE\nAPURO\nAPUSE\nAPUSO\nAQUEA\nAQUEL\nAQUEO\nARABA\nARABE\nARABI\nARABO\nARADA\nARADO\nARAIS\nARANA\nARARA\nARARE\nARASA\nARASE\nARAZA\nARANE\nARANO\nARBOL\nARBOR\nARCAD\nARCAN\nARCAR\nARCAS\nARCEA\nARCEN\nARCES\nARCHA\nARCHI\nARCON\nARCOS\nARDAN\nARDAS\nARDEA\nARDED\nARDEN\nARDER\nARDES\nARDIA\nARDID\nARDIL\nARDIO\nARDOR\nARDUA\nARDUO\nAREAS\nARECA\nAREIS\nARELA\nARELE\nARELO\nARENA\nARENE\nARENO\nAREPA\nARETE\nARFAD\nARFAN\nARFAR\nARFAS\nARFEN\nARFES\nARFIL\nARGAN\nARGEL\nARGEN\nARGON\nARGOS\nARGOT\nARGUE\nARGUI\nARIAS\nARICA\nARICO\nARIDA\nARIDO\nARIES\nARIJA\nARIJE\nARIJO\nARILO\nARIOS\nARLAD\nARLAN\nARLAR\nARLAS\nARLEN\nARLES\nARLOS\nARMAD\nARMAN\nARMAR\nARMAS\nARMEN\nARMES\nARMON\nARMOS\nARNAS\nARNES\nAROCA\nAROMA\nAROME\nAROMO\nARPAD\nARPAN\nARPAR\nARPAS\nARPEN\nARPEO\nARPES\nARPIA\nARPON\nARQUE\nARRAS\nARRAZ\nARREA\nARREE\nARREO\nARRES\nARRIA\nARRIE\nARRIO\nARROZ\nARRUA\nARRUE\nARRUI\nARRUO\nARTAL\nARTAS\nARTES\nARTOS\nARULA\nARUPO\nARUNA\nARUNE\nARUNO\nARZON\nASABA\nASACA\nASACO\nASADA\nASADO\nASAIS\nASARA\nASARE\nASARO\nASASE\nASCAR\nASCAS\nASCIA\nASCIO\nASCOS\nASCUA\nASEAD\nASEAN\nASEAR\nASEAS\nASEDA\nASEDE\nASEDO\nASEEN\nASEES\nASEIS\nASELA\nASELE\nASELO\nASEOS\nASESA\nASESE\nASESO\nASGAN\nASGAS\nASIAN\nASIAS\nASICA\nASICO\nASIDA\nASIDO\nASILA\nASILE\nASILO\nASINA\nASIRA\nASIRE\nASMAR\nASMAS\nASNAL\nASNAS\nASNOS\nASOLA\nASOLE\nASOLO\nASOMA\nASOME\nASOMO\nASONA\nASONE\nASONO\nASPAD\nASPAN\nASPAR\nASPAS\nASPEA\nASPEE\nASPEN\nASPEO\nASPES\nASPIC\nASPID\nASPRO\nASTAS\nASTER\nASTIL\nASTRO\nASTUR\nASUMA\nASUME\nASUMI\nASUMO\nASURA\nASURE\nASURO\nASUSO\nATABA\nATABE\nATACA\nATACO\nATADA\nATADO\nATAIS\nATAJA\nATAJE\nATAJO\nATAPA\nATAPE\nATAPO\nATARA\nATARE\nATASE\nATAUD\nATANA\nATANE\nATANO\nATEAR\nATEAS\nATECE\nATEIS\nATEJE\nATEOS\nATERI\nATESA\nATESE\nATESO\nATETA\nATETE\nATETO\nATEZA\nATEZO\nATIBA\nATIBE\nATIBO\nATICA\nATICE\nATICO\nATINA\nATINE\nATINO\nATIPA\nATIPE\nATIPO\nATIZA\nATIZO\nATLAS\nATOAD\nATOAN\nATOAR\nATOAS\nATOBA\nATOBE\nATOBO\nATOEN\nATOES\nATOJA\nATOJE\nATOJO\nATOLE\nATOMO\nATONA\nATONO\nATORA\nATORE\nATORO\nATRAE\nATRAS\nATRIL\nATRIO\nATROZ\nATUFA\nATUFE\nATUFO\nATURA\nATURE\nATURO\nATUSA\nATUSE\nATUSO\nATUVE\nATUVO\nAUCAS\nAUDAZ\nAUDIO\nAUGES\nAUGUR\nAULAS\nAULLA\nAULLE\nAULLO\nAUNAD\nAUNAN\nAUNAR\nAUNAS\nAUNEN\nAUNES\nAUPAD\nAUPAN\nAUPAR\nAUPAS\nAUPEN\nAUPES\nAURAS\nAUREA\nAUREO\nAUSOL\nAUTAN\nAUTOR\nAUTOS\nAVADA\nAVADE\nAVADO\nAVAHA\nAVAHE\nAVAHO\nAVALA\nAVALE\nAVALO\nAVARA\nAVARO\nAVATI\nAVECE\nAVENA\nAVENE\nAVENI\nAVENO\nAVEZA\nAVEZO\nAVIAD\nAVIAN\nAVIAR\nAVIAS\nAVICA\nAVIDA\nAVIDO\nAVIEN\nAVIES\nAVINE\nAVINO\nAVION\nAVIOS\nAVISA\nAVISE\nAVISO\nAVIVA\nAVIVE\nAVIVO\nAVOCA\nAVOCO\nAVUGO\nAXIAL\nAXILA\nAYACO\nAYATE\nAYEAD\nAYEAN\nAYEAR\nAYEAS\nAYEEN\nAYEES\nAYORA\nAYOTE\nAYUAS\nAYUDA\nAYUDE\nAYUDO\nAYUGA\nAYUNA\nAYUNE\nAYUNO\nAYUSO\nAZADA\nAZAGA\nAZAGO\nAZALA\nAZARA\nAZARE\nAZARO\nAZCON\nAZERI\nAZIMO\nAZOAD\nAZOAN\nAZOAR\nAZOAS\nAZOCA\nAZOCO\nAZOEN\nAZOES\nAZOGA\nAZOGO\nAZOLA\nAZOLE\nAZOLO\nAZORA\nAZORE\nAZORO\nAZOTA\nAZOTE\nAZOTO\nAZTOR\nAZUAS\nAZUCE\nAZUDA\nAZULA\nAZULE\nAZULO\nAZUTS\nAZUZA\nAZUZO\nANADA\nANADI\nANADO\nANEDA\nANEDE\nANEDI\nANEDO\nANERA\nANERO\nANIDI\nANILA\nANILE\nANILO\nANOJA\nANOJO\nANORA\nANORE\nANORO\nANOSA\nANOSO\nBABAS\nBABEA\nBABEE\nBABEL\nBABEO\nBABIS\nBABLE\nBABOR\nBACAN\nBACAS\nBACHE\nBACIA\nBACIN\nBACON\nBADAL\nBADAN\nBADAS\nBADEA\nBADEN\nBADIL\nBAFLE\nBAGAD\nBAGAN\nBAGAR\nBAGAS\nBAGOS\nBAGRE\nBAGUE\nBAHAI\nBAHIA\nBAIDA\nBAIFA\nBAIFO\nBAILA\nBAILE\nBAILO\nBAJAD\nBAJAN\nBAJAR\nBAJAS\nBAJEA\nBAJEE\nBAJEL\nBAJEN\nBAJEO\nBAJES\nBAJEZ\nBAJIA\nBAJIN\nBAJIO\nBAJON\nBAJOS\nBALAD\nBALAJ\nBALAN\nBALAR\nBALAS\nBALAY\nBALDA\nBALDE\nBALDO\nBALEA\nBALEE\nBALEN\nBALEO\nBALES\nBALIN\nBALON\nBALOS\nBALSA\nBALSO\nBALTA\nBALTO\nBAMBA\nBAMBU\nBANAL\nBANAS\nBANCA\nBANCE\nBANCO\nBANDA\nBANDO\nBANIR\nBANJO\nBANTU\nBANYO\nBANZO\nBAQUE\nBARBA\nBARBE\nBARBO\nBARCA\nBARCO\nBARDA\nBARDE\nBARDO\nBARES\nBARIA\nBARIL\nBARIO\nBARIS\nBARNS\nBARON\nBAROS\nBARRA\nBARRE\nBARRI\nBARRO\nBARZA\nBASAD\nBASAL\nBASAN\nBASAR\nBASAS\nBASCA\nBASEN\nBASES\nBASIS\nBASNA\nBASTA\nBASTE\nBASTO\nBATAN\nBATAS\nBATEA\nBATEE\nBATEL\nBATEN\nBATEO\nBATES\nBATEY\nBATIA\nBATID\nBATIN\nBATIO\nBATIR\nBATIS\nBATON\nBATOS\nBATUA\nBAULA\nBAURE\nBAUSA\nBAUZA\nBAYAL\nBAYAS\nBAYON\nBAYOS\nBAYUA\nBAYUS\nBAZAR\nBAZAS\nBAZOS\nBANAD\nBANAN\nBANAR\nBANEN\nBANES\nBANIL\nBANOS\nBEATA\nBEATO\nBEBAN\nBEBAS\nBEBED\nBEBEN\nBEBER\nBEBES\nBEBIA\nBEBIO\nBECAD\nBECAN\nBECAR\nBECAS\nBEDEL\nBEFAD\nBEFAN\nBEFAR\nBEFAS\nBEFEN\nBEFES\nBEFOS\nBEFRE\nBEGUM\nBEIGE\nBEJIN\nBELDA\nBELDE\nBELDO\nBELEN\nBELES\nBELEZ\nBELFA\nBELFO\nBELGA\nBELIO\nBELLA\nBELLO\nBELUA\nBEMBA\nBEMBE\nBEMBO\nBEMOL\nBENES\nBEODA\nBEODO\nBEORI\nBEQUE\nBERBI\nBERMA\nBERON\nBERRA\nBERRE\nBERRO\nBERTA\nBERZA\nBESAD\nBESAN\nBESAR\nBESAS\nBESEN\nBESES\nBESOS\nBETAS\nBETEL\nBETUN\nBEUDA\nBEUDO\nBEYES\nBEZAR\nBEZON\nBEZOS\nBIAZA\nBIBIS\nBICAL\nBICHA\nBICHE\nBICHO\nBICIS\nBICOS\nBIDES\nBIDON\nBIELA\nBIFAZ\nBIFES\nBIGAS\nBIJAO\nBIJAS\nBIJOL\nBILAO\nBILES\nBILIS\nBILLA\nBILMA\nBILME\nBILMO\nBIMBA\nBINAD\nBINAN\nBINAR\nBINAS\nBINEA\nBINEE\nBINEN\nBINEO\nBINES\nBINGO\nBINZA\nBIOTA\nBIRAS\nBIRLA\nBIRLE\nBIRLI\nBIRLO\nBIROS\nBISAD\nBISAN\nBISAR\nBISAS\nBISEL\nBISEN\nBISES\nBISOS\nBISTE\nBITAD\nBITAN\nBITAR\nBITAS\nBITEN\nBITER\nBITES\nBITOR\nBIZAS\nBIZCA\nBIZCO\nBIZMA\nBIZME\nBIZMO\nBIZNA\nBLAOS\nBLAVA\nBLAVO\nBLEDA\nBLEDO\nBLOCA\nBLOCO\nBLOCS\nBLUES\nBLUSA\nBOATO\nBOBAS\nBOBEA\nBOBEE\nBOBEO\nBOBOS\nBOCAL\nBOCAS\nBOCEA\nBOCEE\nBOCEL\nBOCEO\nBOCHA\nBOCHE\nBOCHO\nBOCIN\nBOCIO\nBOCON\nBOCOY\nBODAS\nBODES\nBODON\nBOFAN\nBOFAR\nBOFAS\nBOFEN\nBOFES\nBOFIA\nBOFOS\nBOGAD\nBOGAN\nBOGAR\nBOGAS\nBOGUE\nBOHIO\nBOINA\nBOIRA\nBOITE\nBOJAD\nBOJAN\nBOJAR\nBOJAS\nBOJEA\nBOJEE\nBOJEN\nBOJEO\nBOJES\nBOJOS\nBOLAR\nBOLAS\nBOLDO\nBOLEA\nBOLEE\nBOLEO\nBOLES\nBOLIN\nBOLIS\nBOLLA\nBOLLE\nBOLLO\nBOLON\nBOLOS\nBOLSA\nBOLSO\nBOMBA\nBOMBE\nBOMBO\nBONAL\nBONES\nBONGA\nBONGO\nBONOS\nBONZO\nBOQUE\nBOQUI\nBORAX\nBORDA\nBORDE\nBORDO\nBOREO\nBORIA\nBORLA\nBORNA\nBORNE\nBORNI\nBOROS\nBORRA\nBORRE\nBORRO\nBORTO\nBOSAR\nBOSON\nBOSTA\nBOTAD\nBOTAN\nBOTAR\nBOTAS\nBOTEA\nBOTEE\nBOTEN\nBOTEO\nBOTES\nBOTIN\nBOTON\nBOTOR\nBOTOS\nBOXEA\nBOXEE\nBOXEO\nBOXER\nBOXES\nBOYAD\nBOYAL\nBOYAN\nBOYAR\nBOYAS\nBOYEN\nBOYES\nBOZAL\nBOZAS\nBOZON\nBOZOS\nBRACA\nBRACO\nBRAGA\nBRAMA\nBRAME\nBRAMO\nBRASA\nBRAVA\nBRAVO\nBRAZA\nBRAZO\nBRANA\nBREAD\nBREAN\nBREAR\nBREAS\nBRECA\nBRECE\nBRECO\nBREEN\nBREES\nBREGA\nBREGO\nBRETE\nBREVA\nBREVE\nBREZA\nBREZO\nBRENA\nBRIAL\nBRIBA\nBRICE\nBRIDA\nBRIOL\nBRIOS\nBRISA\nBRISE\nBRISO\nBRIZA\nBRIZO\nBROAS\nBROCA\nBROCE\nBROMA\nBROME\nBROMO\nBROTA\nBROTE\nBROTO\nBROZA\nBROZO\nBRUCE\nBRUGO\nBRUJA\nBRUJE\nBRUJI\nBRUJO\nBRUMA\nBRUME\nBRUMO\nBRUNA\nBRUNO\nBRUTA\nBRUTO\nBRUTS\nBRUZA\nBRUZO\nBRUNE\nBRUNI\nBUARO\nBUBAS\nBUBIS\nBUBON\nBUCAL\nBUCEA\nBUCEE\nBUCEN\nBUCEO\nBUCES\nBUCHE\nBUCIO\nBUCLE\nBUCOS\nBUDAS\nBUDIN\nBUEGA\nBUENA\nBUENO\nBUERA\nBUFAD\nBUFAN\nBUFAR\nBUFAS\nBUFEN\nBUFEO\nBUFES\nBUFET\nBUFIA\nBUFON\nBUFOS\nBUGLE\nBUHIO\nBUHOS\nBUIDA\nBUIDO\nBUJEO\nBUJES\nBUJIA\nBUJOS\nBULAR\nBULAS\nBULBO\nBULDA\nBULES\nBULIN\nBULIS\nBULLA\nBULLE\nBULLI\nBULLO\nBULON\nBULOS\nBULTO\nBUNAS\nBUNIO\nBUQUE\nBURAS\nBURDA\nBURDO\nBUREL\nBUREO\nBURGA\nBURGO\nBURIL\nBURIO\nBURIS\nBURLA\nBURLE\nBURLO\nBUROS\nBURRA\nBURRO\nBUSCA\nBUSCO\nBUSES\nBUSTO\nBUTEN\nBUTIA\nBUYOS\nBUZAD\nBUZAN\nBUZAR\nBUZAS\nBUZON\nBUZOS\nCABAL\nCABAS\nCABED\nCABEN\nCABER\nCABES\nCABIA\nCABIO\nCABLE\nCABOS\nCABRA\nCABRE\nCABRO\nCACAN\nCACAO\nCACAS\nCACEA\nCACEE\nCACEN\nCACEO\nCACES\nCACHA\nCACHE\nCACHO\nCACHU\nCACLE\nCACOS\nCACTO\nCACUY\nCADAS\nCADIS\nCADOS\nCAEIS\nCAENA\nCAERA\nCAERE\nCAFES\nCAFIZ\nCAFRE\nCAGAD\nCAGAN\nCAGAR\nCAGAS\nCAGON\nCAGUE\nCAHIZ\nCAIAN\nCAIAS\nCAICO\nCAIDA\nCAIDO\nCAIES\nCAIGA\nCAIGO\nCAIMA\nCAIRE\nCAITE\nCAJAS\nCAJEL\nCAJIN\nCAJIS\nCAJON\nCAJOS\nCALAD\nCALAN\nCALAO\nCALAR\nCALAS\nCALCA\nCALCE\nCALCO\nCALDA\nCALDO\nCALED\nCALEN\nCALER\nCALES\nCALIA\nCALIO\nCALIS\nCALIZ\nCALLA\nCALLE\nCALLO\nCALMA\nCALME\nCALMO\nCALON\nCALOR\nCALOS\nCALTA\nCALVA\nCALVE\nCALVO\nCALZA\nCALZO\nCAMAL\nCAMAO\nCAMAS\nCAMBA\nCAMBE\nCAMBO\nCAMIO\nCAMON\nCAMPA\nCAMPE\nCAMPO\nCANAL\nCANAS\nCANDA\nCANDE\nCANDI\nCANDO\nCANEA\nCANEE\nCANEO\nCANES\nCANEY\nCANEZ\nCANGA\nCANGO\nCANIA\nCANIL\nCANJE\nCANOA\nCANON\nCANOS\nCANSA\nCANSE\nCANSO\nCANTA\nCANTE\nCANTO\nCANTU\nCAOBA\nCAOBO\nCAPAD\nCAPAN\nCAPAR\nCAPAS\nCAPAZ\nCAPEA\nCAPEE\nCAPEL\nCAPEN\nCAPEO\nCAPES\nCAPIA\nCAPIN\nCAPIO\nCAPIS\nCAPON\nCAPOS\nCAPPA\nCAPTA\nCAPTE\nCAPTO\nCAPUZ\nCAQUI\nCARAO\nCARAS\nCARAU\nCARAY\nCARBA\nCARCA\nCARDA\nCARDE\nCARDO\nCAREA\nCAREE\nCAREL\nCAREO\nCARES\nCAREY\nCARGA\nCARGO\nCARIA\nCARIE\nCARIO\nCARIS\nCARIZ\nCARLA\nCARLO\nCARME\nCARNE\nCARON\nCAROS\nCARPA\nCARPE\nCARPI\nCARPO\nCARRA\nCARRO\nCARTA\nCARVI\nCASAD\nCASAL\nCASAN\nCASAR\nCASAS\nCASCA\nCASCO\nCASEA\nCASEN\nCASEO\nCASES\nCASIA\nCASIS\nCASON\nCASOS\nCASPA\nCASTA\nCASTO\nCATAD\nCATAN\nCATAR\nCATAS\nCATEA\nCATEE\nCATEN\nCATEO\nCATES\nCATEY\nCATIN\nCATON\nCATOS\nCATRE\nCAUBA\nCAUCA\nCAUCE\nCAUDA\nCAUJE\nCAULA\nCAUNO\nCAURI\nCAURO\nCAUSA\nCAUSE\nCAUSO\nCAUTA\nCAUTO\nCAUZA\nCAVAD\nCAVAN\nCAVAR\nCAVAS\nCAVEA\nCAVEN\nCAVES\nCAVIA\nCAVIO\nCAVIS\nCAVON\nCAVOS\nCAYAN\nCAYAS\nCAYOS\nCAZAD\nCAZAN\nCAZAR\nCAZAS\nCAZON\nCAZOS\nCAZUZ\nCANAD\nCANAN\nCANAR\nCANEN\nCANIS\nCEAJA\nCEAJO\nCEBAD\nCEBAN\nCEBAR\nCEBAS\nCEBEN\nCEBES\nCEBIL\nCEBON\nCEBOS\nCEBRA\nCEBRO\nCEBTI\nCEBUS\nCECAL\nCECAS\nCECEA\nCECEE\nCECEO\nCEDAN\nCEDAS\nCEDED\nCEDEN\nCEDER\nCEDES\nCEDIA\nCEDIO\nCEDRO\nCEFEA\nCEFEE\nCEFEO\nCEFOS\nCEGAD\nCEGAR\nCEGAS\nCEGUA\nCEGUE\nCEIBA\nCEIBO\nCEJAD\nCEJAN\nCEJAR\nCEJAS\nCEJEN\nCEJES\nCEJOS\nCELAD\nCELAN\nCELAR\nCELAS\nCELDA\nCELEN\nCELES\nCELFO\nCELIA\nCELLA\nCELLO\nCELOS\nCELTA\nCEMAS\nCEMBO\nCENAD\nCENAL\nCENAN\nCENAR\nCENAS\nCENCA\nCENCO\nCENEN\nCENES\nCENIA\nCENIS\nCENIT\nCENSA\nCENSE\nCENSO\nCENTS\nCEPAS\nCEPOS\nCEPTI\nCEQUI\nCERAS\nCERCA\nCERCO\nCERDA\nCERDO\nCEREA\nCEREO\nCERIO\nCERNA\nCERNE\nCERNI\nCERNO\nCERON\nCEROS\nCERPA\nCERRA\nCERRE\nCERRO\nCESAD\nCESAN\nCESAR\nCESAS\nCESEN\nCESES\nCESIO\nCESTA\nCESTO\nCETIL\nCETIS\nCETME\nCETRA\nCETRE\nCETRO\nCEUTI\nCENID\nCENIR\nCENOS\nCHACA\nCHACE\nCHACO\nCHAFA\nCHAFE\nCHAFO\nCHAIS\nCHAJA\nCHALA\nCHALE\nCHALO\nCHAMA\nCHAME\nCHAMO\nCHANA\nCHANE\nCHANO\nCHAPA\nCHAPE\nCHAPO\nCHATA\nCHATO\nCHAUL\nCHAUZ\nCHAVA\nCHAVE\nCHAVO\nCHAYA\nCHAYE\nCHAYO\nCHAZA\nCHAZO\nCHECA\nCHECO\nCHEFS\nCHEJE\nCHELA\nCHELE\nCHELI\nCHELO\nCHEPA\nCHEPE\nCHEPO\nCHERA\nCHERO\nCHESA\nCHESO\nCHETA\nCHETO\nCHIAD\nCHIAN\nCHIAR\nCHIAS\nCHICA\nCHICO\nCHICS\nCHIDA\nCHIDO\nCHIEN\nCHIES\nCHIFA\nCHIIS\nCHILE\nCHIMA\nCHIME\nCHIMO\nCHIMU\nCHINA\nCHINE\nCHINO\nCHIPA\nCHIPE\nCHIPS\nCHIRA\nCHIRI\nCHISA\nCHIST\nCHITA\nCHITE\nCHITO\nCHIVA\nCHIVE\nCHIVO\nCHIZA\nCHOBA\nCHOCA\nCHOCO\nCHOFE\nCHOLA\nCHOLO\nCHONA\nCHONO\nCHOPA\nCHOPE\nCHOPO\nCHORA\nCHORE\nCHORI\nCHORO\nCHOTA\nCHOTE\nCHOTO\nCHOVA\nCHOYA\nCHOYE\nCHOYO\nCHOZA\nCHOZO\nCHUAS\nCHUCA\nCHUCE\nCHUCO\nCHUFA\nCHUFE\nCHUFO\nCHULA\nCHULE\nCHULO\nCHUNA\nCHUPA\nCHUPE\nCHUPO\nCHURA\nCHURO\nCHURU\nCHUTA\nCHUTE\nCHUTO\nCHUTS\nCHUVA\nCHUYA\nCHUYO\nCHUZA\nCHUZO\nCHUNO\nCIABA\nCIADO\nCIAIS\nCIANI\nCIARA\nCIARE\nCIASE\nCIATO\nCIBAL\nCIBIS\nCICAS\nCICCA\nCICLA\nCICLE\nCICLO\nCIDES\nCIDRA\nCIDRO\nCIECA\nCIEGA\nCIEGO\nCIEIS\nCIELO\nCIEMO\nCIENO\nCIFRA\nCIFRE\nCIFRO\nCIGUA\nCIJAS\nCILIO\nCILLA\nCIMAR\nCIMAS\nCIMBA\nCIMIA\nCIMPA\nCINAS\nCINCA\nCINCO\nCINCS\nCINES\nCINIA\nCINTA\nCINTE\nCINTO\nCIPES\nCIPOS\nCIRCA\nCIRCE\nCIRCO\nCIRIO\nCIRRO\nCISCA\nCISCO\nCISMA\nCISME\nCISMO\nCISNE\nCISTA\nCITAD\nCITAN\nCITAR\nCITAS\nCITEN\nCITES\nCITRA\nCIVIL\nCINAN\nCINEN\nCLACO\nCLACS\nCLAMA\nCLAME\nCLAMO\nCLAPA\nCLARA\nCLARO\nCLASE\nCLAVA\nCLAVE\nCLAVO\nCLEMA\nCLERO\nCLICA\nCLICS\nCLIMA\nCLIPS\nCLISA\nCLISE\nCLISO\nCLOCA\nCLOCO\nCLONA\nCLONE\nCLONO\nCLORA\nCLORE\nCLORO\nCLOTA\nCLUBE\nCLUBS\nCOANA\nCOATI\nCOBAS\nCOBEA\nCOBEZ\nCOBIL\nCOBLA\nCOBOS\nCOBRA\nCOBRE\nCOBRO\nCOCAD\nCOCAL\nCOCAN\nCOCAR\nCOCAS\nCOCEA\nCOCED\nCOCEE\nCOCEO\nCOCER\nCOCES\nCOCHA\nCOCHE\nCOCHI\nCOCHO\nCOCIA\nCOCIO\nCOCOL\nCOCOS\nCOCUI\nCOCUY\nCODAL\nCODAS\nCODEA\nCODEE\nCODEO\nCODEZ\nCODIN\nCODON\nCODOS\nCOEVA\nCOEVO\nCOFAN\nCOFAS\nCOFIA\nCOFIN\nCOFRE\nCOGED\nCOGEN\nCOGER\nCOGES\nCOGIA\nCOGIO\nCOGON\nCOIMA\nCOIME\nCOINE\nCOIPO\nCOITA\nCOITE\nCOITO\nCOJAL\nCOJAN\nCOJAS\nCOJEA\nCOJEE\nCOJEO\nCOJIN\nCOJON\nCOJOS\nCOLAD\nCOLAN\nCOLAR\nCOLAS\nCOLEA\nCOLEE\nCOLEN\nCOLEO\nCOLES\nCOLGA\nCOLGO\nCOLIN\nCOLLA\nCOLMA\nCOLME\nCOLMO\nCOLON\nCOLOR\nCOLOS\nCOLPA\nCOLPE\nCOLZA\nCOMAL\nCOMAN\nCOMAS\nCOMBA\nCOMBE\nCOMBO\nCOMED\nCOMEN\nCOMER\nCOMES\nCOMIA\nCOMIC\nCOMIO\nCOMIS\nCOMTA\nCOMTO\nCOMUN\nCONCA\nCONDE\nCONGA\nCONGO\nCONOS\nCONTA\nCONTE\nCONTO\nCOONA\nCOPAD\nCOPAL\nCOPAN\nCOPAR\nCOPAS\nCOPEA\nCOPEC\nCOPEE\nCOPEN\nCOPEO\nCOPES\nCOPEY\nCOPIA\nCOPIE\nCOPIN\nCOPIO\nCOPLA\nCOPON\nCOPOS\nCOPRA\nCOPTA\nCOPTO\nCOQUE\nCOQUI\nCORAD\nCORAL\nCORAN\nCORAR\nCORAS\nCORBE\nCORCA\nCORCO\nCORDA\nCOREA\nCOREE\nCOREN\nCOREO\nCORES\nCORIO\nCORIS\nCORLA\nCORLE\nCORLO\nCORMA\nCORNO\nCOROS\nCORPA\nCORPS\nCORRA\nCORRE\nCORRI\nCORRO\nCORSA\nCORSE\nCORSO\nCORTA\nCORTE\nCORTO\nCORUA\nCORVA\nCORVE\nCORVO\nCORZA\nCORZO\nCOSAN\nCOSAS\nCOSCA\nCOSCO\nCOSED\nCOSEN\nCOSER\nCOSES\nCOSIA\nCOSIO\nCOSOS\nCOSPE\nCOSTA\nCOSTE\nCOSTO\nCOTAD\nCOTAN\nCOTAR\nCOTAS\nCOTEN\nCOTES\nCOTIN\nCOTIS\nCOTON\nCOTOS\nCOTUA\nCOVAD\nCOVAN\nCOVAR\nCOVAS\nCOVEN\nCOVES\nCOXAL\nCOXAS\nCOXIS\nCOYAN\nCOYAS\nCOYES\nCOYOL\nCONAC\nCONAS\nCONEA\nCONEE\nCONEO\nCONON\nCRACS\nCRASA\nCRASO\nCRAZA\nCREAD\nCREAN\nCREAR\nCREAS\nCRECE\nCRECI\nCREDO\nCREED\nCREEN\nCREER\nCREES\nCREIA\nCREMA\nCREME\nCREMO\nCREPE\nCREPS\nCRESA\nCRESO\nCRETA\nCREYO\nCRIAD\nCRIAN\nCRIAR\nCRIAS\nCRIBA\nCRIBE\nCRIBO\nCRICA\nCRICS\nCRIDA\nCRIEN\nCRIES\nCRINA\nCRINE\nCRINO\nCRIOS\nCROAD\nCROAN\nCROAR\nCROAS\nCROCO\nCROEN\nCROES\nCROMA\nCROME\nCROMO\nCRONO\nCROSS\nCROTO\nCROZA\nCRUCE\nCRUDA\nCRUDO\nCRUEL\nCRUJA\nCRUJE\nCRUJI\nCRUJO\nCRUOR\nCRUPS\nCRUZA\nCRUZO\nCUABA\nCUACO\nCUADA\nCUADO\nCUAJA\nCUAJE\nCUAJO\nCUAPE\nCUASI\nCUATA\nCUATE\nCUATI\nCUBAS\nCUBIL\nCUBOS\nCUBRA\nCUBRE\nCUBRI\nCUBRO\nCUCAD\nCUCAN\nCUCAR\nCUCAS\nCUCHA\nCUCHE\nCUCHI\nCUCHO\nCUCOS\nCUCUS\nCUCUY\nCUECA\nCUECE\nCUECO\nCUELA\nCUELE\nCUELO\nCUERA\nCUERO\nCUETE\nCUETO\nCUEVA\nCUEZA\nCUEZO\nCUICA\nCUICO\nCUIDA\nCUIDE\nCUIDO\nCUIJA\nCUILO\nCUINA\nCUINO\nCUITA\nCUJAS\nCUJES\nCUJIN\nCUJIS\nCUJON\nCULAR\nCULAS\nCULEA\nCULEE\nCULEN\nCULEO\nCULIA\nCULIO\nCULIS\nCULLE\nCULON\nCULOS\nCULPA\nCULPE\nCULPO\nCULTA\nCULTO\nCUMAS\nCUMBA\nCUMBE\nCUMBO\nCUMEL\nCUMPA\nCUNAD\nCUNAN\nCUNAR\nCUNAS\nCUNDA\nCUNDE\nCUNDI\nCUNDO\nCUNEA\nCUNEE\nCUNEN\nCUNEO\nCUNES\nCUOTA\nCUPES\nCUPLE\nCUPON\nCUPOS\nCUQUE\nCURAD\nCURAL\nCURAN\nCURAR\nCURAS\nCURCA\nCURCO\nCURDA\nCURDO\nCUREN\nCURES\nCURIA\nCURIE\nCURIL\nCURIO\nCURIS\nCUROS\nCURRA\nCURRE\nCURRO\nCURRY\nCURSA\nCURSE\nCURSI\nCURSO\nCURTA\nCURTE\nCURTI\nCURTO\nCURUL\nCURVA\nCURVE\nCURVO\nCUSAN\nCUSAS\nCUSCA\nCUSCO\nCUSCU\nCUSEN\nCUSES\nCUSIA\nCUSID\nCUSIO\nCUSIR\nCUSIS\nCUSMA\nCUSPA\nCUSUL\nCUTAS\nCUTER\nCUTES\nCUTIO\nCUTIR\nCUTIS\nCUTOS\nCUTRA\nCUTRE\nCUYAS\nCUYEO\nCUYES\nCUYOS\nCUZAS\nCUZCO\nCUZMA\nCUZOS\nCUZUL\nCUNAL\nCUNOS\nDABAN\nDABAS\nDABLE\nDACHA\nDACIA\nDACIO\nDADAS\nDADOR\nDADOS\nDAGAS\nDAHIR\nDAIFA\nDAJAO\nDALAS\nDALGO\nDALIA\nDALLA\nDALLE\nDALLO\nDAMAS\nDAMIL\nDAMOS\nDANCE\nDANDI\nDANDO\nDANES\nDANGO\nDANTA\nDANTE\nDANTO\nDANZA\nDANZO\nDAQUI\nDARAN\nDARAS\nDARDO\nDARES\nDARGA\nDARIA\nDATAD\nDATAN\nDATAR\nDATAS\nDATEA\nDATEE\nDATEN\nDATEO\nDATES\nDATIL\nDATOS\nDAUCO\nDAUDA\nDAZAS\nDANAD\nDANAN\nDANAR\nDANAS\nDANEN\nDANOS\nDEBAN\nDEBAS\nDEBDA\nDEBDO\nDEBED\nDEBEN\nDEBER\nDEBES\nDEBIA\nDEBIL\nDEBIO\nDEBLA\nDEBOS\nDEBUT\nDECAE\nDECAI\nDECIA\nDECID\nDECIR\nDECIS\nDECOR\nDEDAL\nDEDEO\nDEDIL\nDEDOS\nDEESA\nDEJAD\nDEJAN\nDEJAR\nDEJAS\nDEJEN\nDEJES\nDEJOS\nDELCO\nDELES\nDELGA\nDELIA\nDELIO\nDELLA\nDELLO\nDELTA\nDEMAS\nDEMOS\nDENDE\nDENSA\nDENSO\nDENTA\nDENTE\nDENTO\nDEPON\nDEQUE\nDERBI\nDESCA\nDESDA\nDESDE\nDESDI\nDESEA\nDESEE\nDESEO\nDESES\nDESGA\nDESOI\nDESTA\nDESTE\nDESTO\nDESUS\nDETAL\nDETEN\nDEUDA\nDEUDO\nDEVEN\nDEYES\nDEZMA\nDEZME\nDEZMO\nDENAR\nDIADA\nDIADO\nDIANA\nDIANO\nDICAZ\nDICEN\nDICES\nDICHA\nDICHO\nDICTA\nDICTE\nDICTO\nDIEGO\nDIERA\nDIERE\nDIESE\nDIESI\nDIETA\nDIETE\nDIETO\nDIGAN\nDIGAS\nDIGNA\nDIGNE\nDIGNO\nDIJES\nDILUI\nDIMAN\nDIMAS\nDIMEN\nDIMES\nDIMIA\nDIMID\nDIMIO\nDIMIR\nDIMIS\nDIMOS\nDINAR\nDINAS\nDINES\nDINOS\nDIODO\nDIOSA\nDIOSO\nDIQUE\nDIRAN\nDIRAS\nDIRIA\nDISCA\nDISCO\nDISON\nDISTA\nDISTE\nDISTO\nDITAS\nDIUCA\nDIVAN\nDIVAS\nDIVOS\nDINAD\nDINAN\nDINEN\nDOBLA\nDOBLE\nDOBLO\nDOCAS\nDOCES\nDOCIL\nDOCTA\nDOCTO\nDODOS\nDOGAL\nDOGAS\nDOGMA\nDOGOS\nDOGRE\nDOLAD\nDOLAR\nDOLAS\nDOLED\nDOLER\nDOLES\nDOLIA\nDOLIO\nDOLOR\nDOLOS\nDOMAD\nDOMAN\nDOMAR\nDOMAS\nDOMBO\nDOMEN\nDOMES\nDOMOS\nDONAD\nDONAN\nDONAR\nDONAS\nDONDE\nDONEN\nDONEO\nDONES\nDOPAD\nDOPAN\nDOPAR\nDOPAS\nDOPEN\nDOPES\nDORAD\nDORAL\nDORAN\nDORAR\nDORAS\nDOREN\nDORES\nDORIA\nDORIO\nDORMI\nDORNA\nDORSO\nDOSEL\nDOSES\nDOSIS\nDOTAD\nDOTAL\nDOTAN\nDOTAR\nDOTAS\nDOTEN\nDOTES\nDOTOR\nDONEA\nDONEE\nDRABA\nDRAGA\nDRAGO\nDRAMA\nDREAS\nDRENA\nDRENE\nDRENO\nDRIAS\nDRINO\nDRIZA\nDROGA\nDROGO\nDROPE\nDRUPA\nDRUSA\nDRUSO\nDSEDA\nDUBAS\nDUBDA\nDUBIO\nDUCAL\nDUCAS\nDUCES\nDUCHA\nDUCHE\nDUCHO\nDUCOS\nDUCTO\nDUDAD\nDUDAN\nDUDAR\nDUDAS\nDUDEN\nDUDES\nDUELA\nDUELE\nDUELO\nDUETO\nDUENA\nDUENO\nDUGOS\nDUJOS\nDULAR\nDULAS\nDULCE\nDULIA\nDUMAN\nDUMAS\nDUMEN\nDUMES\nDUMIA\nDUMID\nDUMIO\nDUMIR\nDUMIS\nDUNAS\nDUNDA\nDUNDO\nDUPLA\nDUPLO\nDUQUE\nDURAD\nDURAN\nDURAR\nDURAS\nDUREN\nDURES\nDUROS\nEBANO\nEBRIA\nEBRIO\nECHAD\nECHAN\nECHAR\nECHAS\nECHEN\nECHES\nECUAS\nECUOS\nEDEMA\nEDILA\nEDITA\nEDITE\nEDITO\nEDRAD\nEDRAN\nEDRAR\nEDRAS\nEDREN\nEDRES\nEDUCA\nEDUCE\nEDUCI\nEDUCO\nEDUJE\nEDUJO\nEFEBO\nEFETA\nEFETO\nEFLUI\nEFORO\nEGENA\nEGENO\nEGIDA\nEGUAR\nEIRAS\nEJIDO\nEJION\nEJOTE\nELAMI\nELATA\nELATO\nELCHE\nELEGA\nELEGI\nELEGO\nELEMI\nELEPE\nELETA\nELETO\nELEVA\nELEVE\nELEVO\nELFOS\nELIDA\nELIDE\nELIDI\nELIDO\nELIGE\nELIJA\nELIJE\nELIJO\nELITE\nELLAS\nELLES\nELLOS\nELOTE\nELUDA\nELUDE\nELUDI\nELUDO\nEMANA\nEMANE\nEMANO\nEMBAI\nEMITA\nEMITE\nEMITI\nEMITO\nEMPOS\nEMPRA\nEMPRE\nEMPRO\nEMUES\nEMULA\nEMULE\nEMULO\nENANA\nENANO\nENCIA\nENEAL\nENEAS\nENEJA\nENEJE\nENEJO\nENEMA\nENEOS\nENERO\nENOJA\nENOJE\nENOJO\nENRIA\nENRIE\nENRIO\nENSAY\nENTEO\nENTES\nENTRA\nENTRE\nENTRO\nENULA\nENVES\nENVIA\nENVIE\nENVIO\nENZAS\nEOLIA\nEOLIO\nEONES\nEPALE\nEPATA\nEPATE\nEPATO\nEPICA\nEPICO\nEPOCA\nEPODA\nEPODO\nEPOTA\nEPOTO\nEPOXI\nEQUIS\nERABA\nERADA\nERADO\nERAIS\nERAJE\nERALA\nERARA\nERARE\nERASE\nERBIO\nERCER\nEREBO\nEREIS\nERGIO\nERGUI\nERIAL\nERIAS\nERICE\nERIGE\nERIGI\nERIJA\nERIJO\nERINA\nERIOS\nERIZA\nERIZO\nERMAR\nEROGA\nEROGO\nERRAD\nERRAJ\nERRAN\nERRAR\nERRAS\nERREN\nERRES\nERROR\nERROS\nERUTA\nERUTE\nERUTO\nESCAS\nESCAY\nESCOA\nESMUI\nESNOB\nESPAY\nESPIA\nESPIE\nESPIN\nESPIO\nESQUI\nESTAD\nESTAN\nESTAR\nESTAS\nESTAY\nESTEN\nESTER\nESTES\nESTIL\nESTIO\nESTOL\nESTOR\nESTOS\nESTOY\nESTRO\nESULA\nETANO\nETAPA\nETICA\nETICO\nETILO\nETIMO\nETNEA\nETNEO\nETNIA\nETOLA\nETOLO\nETUSA\nEUBEA\nEUBEO\nEUROS\nEVADA\nEVADE\nEVADI\nEVADO\nEVITA\nEVITE\nEVITO\nEVOCA\nEVOCO\nEVOHE\nEXIDA\nEXIGE\nEXIGI\nEXIJA\nEXIJO\nEXILA\nEXILE\nEXILO\nEXIMA\nEXIME\nEXIMI\nEXIMO\nEXITO\nEXODO\nEXORA\nEXORE\nEXORO\nEXPIA\nEXPIE\nEXPIO\nEXPON\nEXTRA\nEXUDA\nEXUDE\nEXUDO\nFABAS\nFABLA\nFABOS\nFABRO\nFACAS\nFACER\nFACES\nFACHA\nFACHE\nFACHO\nFACIL\nFACON\nFACTO\nFADAS\nFADOS\nFAENA\nFAENE\nFAENO\nFAGOS\nFAGOT\nFAINA\nFAINO\nFAJAD\nFAJAN\nFAJAR\nFAJAS\nFAJEA\nFAJEE\nFAJEN\nFAJEO\nFAJES\nFAJIN\nFAJOL\nFAJON\nFAJOS\nFALAZ\nFALCA\nFALCE\nFALCO\nFALDA\nFALLA\nFALLE\nFALLO\nFALOS\nFALSA\nFALSE\nFALSO\nFALTA\nFALTE\nFALTO\nFALUA\nFAMAS\nFANAL\nFANES\nFANGO\nFAQUI\nFARAD\nFARAS\nFARDA\nFARDE\nFARDO\nFARIA\nFARIO\nFAROL\nFARON\nFAROS\nFARPA\nFARRA\nFARRO\nFARSA\nFARTE\nFASES\nFASOL\nFASOS\nFASTA\nFASTO\nFATAL\nFATAS\nFATOR\nFATOS\nFATUA\nFATUO\nFAUNA\nFAUNO\nFAVOR\nFAVOS\nFAXEA\nFAXEE\nFAXEO\nFAXES\nFAYAS\nFANAD\nFANAN\nFANAR\nFANAS\nFANEN\nFEBEA\nFEBEO\nFEBLE\nFECAL\nFECES\nFECHA\nFECHE\nFECHO\nFEEZA\nFEJES\nFELIZ\nFELON\nFELPA\nFELPE\nFELPO\nFELUS\nFEMAD\nFEMAN\nFEMAR\nFEMAS\nFEMEN\nFEMES\nFEMUR\nFENAL\nFENDA\nFENDI\nFENIX\nFENOL\nFERAL\nFERAZ\nFERIA\nFERIE\nFERIO\nFERIR\nFERMI\nFEROZ\nFERRA\nFERRE\nFERRO\nFERRY\nFESTA\nFETAL\nFETAS\nFETEN\nFETOR\nFETOS\nFETUA\nFEUCA\nFEUCO\nFEUDA\nFEUDE\nFEUDO\nFEURA\nFIABA\nFIACA\nFIADA\nFIADO\nFIAIS\nFIANA\nFIARA\nFIARE\nFIASE\nFIATS\nFIBRA\nFICAR\nFICEN\nFICES\nFICHA\nFICHE\nFICHO\nFICUS\nFIDEO\nFIEIS\nFIEMO\nFIERA\nFIERO\nFIFAD\nFIFAN\nFIFAR\nFIFAS\nFIFEN\nFIFES\nFIFIS\nFIGLE\nFIGON\nFIJAD\nFIJAN\nFIJAR\nFIJAS\nFIJEN\nFIJES\nFIJON\nFIJOS\nFILAD\nFILAN\nFILAR\nFILAS\nFILEN\nFILES\nFILFA\nFILIA\nFILIE\nFILIN\nFILIO\nFILIS\nFILLO\nFILMA\nFILME\nFILMO\nFILMS\nFILON\nFILOS\nFIMOS\nFINAD\nFINAL\nFINAN\nFINAR\nFINAS\nFINCA\nFINCO\nFINEN\nFINES\nFINGE\nFINGI\nFINIA\nFINID\nFINIO\nFINIR\nFINIS\nFINJA\nFINJO\nFINOS\nFINTA\nFINTE\nFINTO\nFIQUE\nFIRMA\nFIRME\nFIRMO\nFISAN\nFISCO\nFISGA\nFISGO\nFISTA\nFISTO\nFIZAD\nFIZAN\nFIZAR\nFIZAS\nFIZON\nFLACA\nFLACO\nFLAMA\nFLAON\nFLASH\nFLATO\nFLAVA\nFLAVO\nFLECO\nFLEJA\nFLEJE\nFLEJO\nFLEMA\nFLEME\nFLEOS\nFLETA\nFLETE\nFLETO\nFLEXO\nFLIPA\nFLIPE\nFLIPO\nFLOJA\nFLOJO\nFLORA\nFLORE\nFLORO\nFLOTA\nFLOTE\nFLOTO\nFLUIA\nFLUID\nFLUIR\nFLUIS\nFLUJO\nFLUOR\nFLUYA\nFLUYE\nFLUYO\nFOBIA\nFOCAL\nFOCAS\nFOCHA\nFOCIA\nFOCIO\nFOCOS\nFOFAS\nFOFOS\nFOGON\nFOISA\nFOISO\nFOJAS\nFOLGA\nFOLGO\nFOLIA\nFOLIE\nFOLIO\nFOLLA\nFOLLE\nFOLLO\nFOLUZ\nFOMES\nFONDA\nFONDO\nFONES\nFONIL\nFONIO\nFONJE\nFONOS\nFOQUE\nFORAL\nFORAS\nFORCA\nFORCE\nFORJA\nFORJE\nFORJO\nFORMA\nFORME\nFORMO\nFORNO\nFOROS\nFORRA\nFORRE\nFORRO\nFORTE\nFORUM\nFORZA\nFORZO\nFOSAD\nFOSAL\nFOSAN\nFOSAR\nFOSAS\nFOSCA\nFOSCO\nFOSEN\nFOSES\nFOSIL\nFOSOR\nFOSOS\nFOTON\nFOTOS\nFOVEA\nFRACS\nFRADA\nFRADE\nFRADO\nFRAGA\nFRASE\nFRANA\nFRANE\nFRANI\nFRANO\nFRECE\nFREDO\nFREGA\nFREGO\nFREIA\nFREID\nFREIR\nFREIS\nFRENA\nFRENE\nFRENO\nFREON\nFREOS\nFRESA\nFRESE\nFRESO\nFRETA\nFRETE\nFRETO\nFREZA\nFREZO\nFRIAN\nFRIAS\nFRICA\nFRICO\nFRIEN\nFRIES\nFRIOR\nFRIOS\nFRISA\nFRISE\nFRISO\nFRITA\nFRITE\nFRITO\nFROGA\nFROGO\nFROTA\nFROTE\nFROTO\nFRUIA\nFRUID\nFRUIR\nFRUIS\nFRUTA\nFRUTE\nFRUTO\nFRUYA\nFRUYE\nFRUYO\nFUCAR\nFUCHI\nFUCIA\nFUCOS\nFUDRE\nFUEGO\nFUERA\nFUERE\nFUERO\nFUESA\nFUESE\nFUETS\nFUFAD\nFUFAN\nFUFAR\nFUFAS\nFUFEN\nFUFES\nFUFOS\nFUFUS\nFUGAN\nFUGAR\nFUGAS\nFUGAZ\nFUGIR\nFUGUE\nFUINA\nFULAR\nFULAS\nFULGE\nFULGI\nFULJA\nFULJO\nFULLA\nFUMAD\nFUMAN\nFUMAR\nFUMAS\nFUMEN\nFUMES\nFUMON\nFUNCA\nFUNCO\nFUNDA\nFUNDE\nFUNDI\nFUNDO\nFUNGE\nFUNGI\nFUNJA\nFUNJO\nFURAS\nFURIA\nFUROR\nFUROS\nFURTO\nFUSAS\nFUSCA\nFUSCO\nFUSIL\nFUSOR\nFUSOS\nFUSTA\nFUSTE\nFUSTO\nFUTIL\nFUTON\nFUTRE\nFUNAR\nGABAN\nGABAR\nGACEL\nGACHA\nGACHE\nGACHI\nGACHO\nGAFAD\nGAFAN\nGAFAR\nGAFAS\nGAFEA\nGAFEE\nGAFEN\nGAFEO\nGAFES\nGAFOS\nGAGAS\nGAGOS\nGAITA\nGAJES\nGAJOS\nGALAN\nGALAS\nGALCE\nGALEA\nGALEO\nGALES\nGALGA\nGALGO\nGALIO\nGALLA\nGALLE\nGALLO\nGALON\nGALOP\nGALOS\nGALUA\nGAMAS\nGAMBA\nGAMMA\nGAMON\nGAMOS\nGANAD\nGANAN\nGANAR\nGANAS\nGANEN\nGANES\nGANGA\nGANSA\nGANSO\nGANTA\nGANTE\nGARAS\nGARAY\nGARBA\nGARBE\nGARBO\nGARFA\nGARIA\nGARIO\nGARLA\nGARLE\nGARLO\nGARMA\nGAROS\nGARPA\nGARPE\nGARPO\nGARRA\nGARRE\nGARRI\nGARRO\nGARUA\nGARUE\nGARUO\nGARZA\nGARZO\nGASAS\nGASEA\nGASEE\nGASEO\nGASES\nGASON\nGASTA\nGASTE\nGASTO\nGATAS\nGATEA\nGATEE\nGATEO\nGATOS\nGAUSS\nGAVIA\nGAYAD\nGAYAN\nGAYAR\nGAYAS\nGAYEN\nGAYES\nGAYOS\nGAZAS\nGAZNA\nGAZNE\nGAZNO\nGANIA\nGANID\nGANIL\nGANIN\nGANIR\nGANIS\nGANON\nGELAN\nGELAR\nGELAS\nGELEN\nGELES\nGELFE\nGEMAS\nGEMIA\nGEMID\nGEMIR\nGEMIS\nGENES\nGENIO\nGENOL\nGENTE\nGEODA\nGERBO\nGESTA\nGESTE\nGESTO\nGETAS\nGIBAD\nGIBAN\nGIBAO\nGIBAR\nGIBAS\nGIBEN\nGIBES\nGIBON\nGIGAS\nGILAS\nGILES\nGILIS\nGILVA\nGILVO\nGIMAN\nGIMAS\nGIMEN\nGIMES\nGIMIO\nGINEA\nGIRAD\nGIRAN\nGIRAR\nGIRAS\nGIREN\nGIRES\nGIROS\nGISES\nGISTE\nGLASE\nGLAYO\nGLEBA\nGLERA\nGLIAL\nGLIAS\nGLIDE\nGLIFO\nGLOBO\nGLOSA\nGLOSE\nGLOSO\nGLUMA\nGNEIS\nGNOMO\nGOBEN\nGOBIO\nGOCEN\nGOCES\nGOCHA\nGOCHO\nGODAS\nGODEO\nGODOS\nGOFAS\nGOFIO\nGOFOS\nGOFRA\nGOFRE\nGOFRO\nGOLAS\nGOLEA\nGOLEE\nGOLEO\nGOLES\nGOLFA\nGOLFO\nGOLFS\nGOLPE\nGOMAR\nGOMAS\nGOMEL\nGOMER\nGOMIA\nGONCE\nGONGO\nGORDA\nGORDO\nGORGA\nGORJA\nGORMA\nGORME\nGORMO\nGORRA\nGORRO\nGOTAS\nGOTEA\nGOTEE\nGOTEO\nGOTON\nGOYAS\nGOYOS\nGOZAD\nGOZAN\nGOZAR\nGOZAS\nGOZNE\nGOZON\nGOZOS\nGRABA\nGRABE\nGRABO\nGRADA\nGRADE\nGRADO\nGRAFO\nGRAIS\nGRAJA\nGRAJO\nGRAMA\nGRAME\nGRAMO\nGRANA\nGRAND\nGRANE\nGRANO\nGRANT\nGRAOS\nGRAPA\nGRAPE\nGRAPO\nGRASA\nGRASO\nGRATA\nGRATE\nGRATO\nGRAVA\nGRAVE\nGRAVO\nGREBA\nGRECA\nGRECO\nGREDA\nGREEN\nGRELO\nGRENO\nGRENA\nGRIAL\nGRIDA\nGRIFA\nGRIFE\nGRIFO\nGRIJA\nGRILL\nGRIMA\nGRIPA\nGRIPE\nGRIPO\nGRISA\nGRISU\nGRITA\nGRITE\nGRITO\nGROAD\nGROAN\nGROAR\nGROAS\nGROEN\nGROES\nGROGS\nGROJO\nGROMO\nGROSA\nGROSO\nGRUAS\nGRUIA\nGRUID\nGRUIR\nGRUIS\nGRUJA\nGRUJE\nGRUJI\nGRUJO\nGRUMO\nGRUPA\nGRUPI\nGRUPO\nGRUTA\nGRUYA\nGRUYE\nGRUYO\nGRUNA\nGRUNE\nGRUNI\nGRUNO\nGUABA\nGUABO\nGUACA\nGUACO\nGUADO\nGUAIS\nGUAJA\nGUAJE\nGUALA\nGUAMA\nGUAME\nGUAMO\nGUANO\nGUAOS\nGUAPA\nGUAPE\nGUAPO\nGUARA\nGUARE\nGUARI\nGUARO\nGUASA\nGUASO\nGUATA\nGUATE\nGUATO\nGUAYA\nGUAYE\nGUAYO\nGUBIA\nGUERA\nGUERO\nGUETO\nGUENA\nGUIAD\nGUIAN\nGUIAR\nGUIAS\nGUIDA\nGUIDO\nGUIEN\nGUIES\nGUIFA\nGUIJA\nGUIJO\nGUILA\nGUILO\nGUINA\nGUINO\nGUION\nGUIPA\nGUIPE\nGUIPO\nGUIRA\nGUIRE\nGUIRI\nGUIRO\nGUISA\nGUISE\nGUISO\nGUITA\nGUITE\nGUITO\nGUIYE\nGUINE\nGUJAS\nGULAG\nGULAR\nGULAS\nGULAY\nGULES\nGUMIA\nGURDA\nGURDO\nGURIS\nGURUS\nGUSTA\nGUSTE\nGUSTO\nGUZGA\nGUZGO\nGUZLA\nHABAR\nHABAS\nHABER\nHABIA\nHABIL\nHABIZ\nHABLA\nHABLE\nHABLO\nHABON\nHABRA\nHABRE\nHABUS\nHACAN\nHACED\nHACEN\nHACER\nHACES\nHACHA\nHACHE\nHACHO\nHACIA\nHADAR\nHADAS\nHADOS\nHAFIZ\nHAGAN\nHAGAS\nHAIGA\nHALAD\nHALAN\nHALAR\nHALAS\nHALDA\nHALEN\nHALES\nHALLA\nHALLE\nHALLO\nHALON\nHALOS\nHAMEZ\nHAMPA\nHAMPO\nHANZO\nHAPAX\nHARAN\nHARAS\nHARBA\nHARBE\nHARBO\nHARCA\nHARDA\nHAREM\nHAREN\nHARIA\nHARMA\nHARON\nHARPA\nHARRE\nHARTA\nHARTE\nHARTO\nHASTA\nHATEA\nHATEE\nHATEO\nHATOS\nHAUTE\nHAVAR\nHAVOS\nHAYAL\nHAYAN\nHAYAS\nHAYOS\nHAZAS\nHEBEN\nHEBRA\nHECES\nHECHA\nHECHO\nHEDED\nHEDER\nHEDES\nHEDIA\nHEDIO\nHEDOR\nHELAD\nHELAR\nHELAS\nHELEA\nHELEE\nHELEO\nHELIO\nHELOR\nHEMOS\nHENAL\nHENAR\nHENDE\nHENDI\nHENIL\nHENOS\nHENRY\nHERBA\nHERBE\nHERBO\nHERIA\nHERID\nHERIL\nHERIR\nHERIS\nHERMA\nHEROE\nHERPE\nHERRA\nHERRE\nHERRO\nHERTZ\nHERVE\nHERVI\nHESPA\nHESPE\nHESPI\nHESPO\nHETEA\nHETEO\nHEVEA\nHENIA\nHENID\nHENIR\nHENIS\nHIATO\nHICOS\nHIDRA\nHIEDA\nHIEDE\nHIEDO\nHIELA\nHIELE\nHIELO\nHIENA\nHIERA\nHIERE\nHIERO\nHIGAS\nHIGOS\nHIGUI\nHIJAS\nHIJEA\nHIJEE\nHIJEO\nHIJOS\nHILAD\nHILAN\nHILAR\nHILAS\nHILEN\nHILES\nHILIO\nHILOS\nHIMEN\nHIMNO\nHIMPA\nHIMPE\nHIMPO\nHINCA\nHINCO\nHINDI\nHINDU\nHIPAD\nHIPAN\nHIPAR\nHIPAS\nHIPEN\nHIPER\nHIPES\nHIPOS\nHIPPY\nHIRCO\nHIRIO\nHIRMA\nHIRME\nHIRMO\nHISCA\nHISPA\nHISPE\nHISPI\nHISPO\nHITAD\nHITAN\nHITAR\nHITAS\nHITEN\nHITES\nHITON\nHITOS\nHINAN\nHINAS\nHINEN\nHINES\nHINIA\nHINID\nHINIR\nHINIS\nHOBBY\nHOBOS\nHOCEN\nHOCES\nHOGAR\nHOGOS\nHOJAS\nHOJEA\nHOJEE\nHOJEO\nHOLAN\nHOLCO\nHOLEA\nHOLEE\nHOLEO\nHOLGA\nHOLGO\nHOLLA\nHOLLE\nHOLLO\nHOMES\nHONDA\nHONDO\nHONGO\nHONOR\nHONRA\nHONRE\nHONRO\nHOPAN\nHOPAR\nHOPAS\nHOPEA\nHOPEE\nHOPEN\nHOPEO\nHOPES\nHOPOS\nHOQUE\nHORAS\nHORCA\nHORCO\nHORDA\nHORMA\nHORNA\nHORNE\nHORNO\nHORRA\nHORRE\nHORRO\nHOSCA\nHOSCO\nHOSPA\nHOSTE\nHOTEL\nHOTOS\nHOVES\nHOYAD\nHOYAN\nHOYAR\nHOYAS\nHOYEN\nHOYES\nHOYOS\nHOZAD\nHOZAN\nHOZAR\nHOZAS\nHUACA\nHUACO\nHUAJE\nHUAOS\nHUCHA\nHUCHO\nHUCIA\nHUECA\nHUECO\nHUEGO\nHUELA\nHUELE\nHUELO\nHUERA\nHUERO\nHUESA\nHUESO\nHUEVA\nHUEVE\nHUEVO\nHUIAN\nHUIAS\nHUICH\nHUIDA\nHUIDO\nHUIFA\nHUILA\nHUILO\nHUIRA\nHUIRE\nHUIRO\nHULAD\nHULAN\nHULAR\nHULAS\nHULEA\nHULEE\nHULEN\nHULEO\nHULES\nHULLA\nHULTE\nHUMAD\nHUMAN\nHUMAR\nHUMAS\nHUMEA\nHUMEE\nHUMEN\nHUMEO\nHUMES\nHUMIL\nHUMOR\nHUMOS\nHUMUS\nHUNAS\nHUNDA\nHUNDE\nHUNDI\nHUNDO\nHUNOS\nHUPES\nHURAS\nHURGA\nHURGO\nHURIS\nHURON\nHURRA\nHURTA\nHURTE\nHURTO\nHUSAR\nHUSMA\nHUSME\nHUSMO\nHUSOS\nHUTAS\nHUTIA\nHUYAN\nHUYAS\nHUYEN\nHUYES\nIBAIS\nIBERA\nIBERO\nIBICE\nICACO\nICEIS\nICHAL\nICHOS\nICHUS\nICONO\nICTUS\nIDEAD\nIDEAL\nIDEAN\nIDEAR\nIDEAS\nIDEAY\nIDEEN\nIDEES\nIDEOS\nIDOLO\nIGLUS\nIGNEA\nIGNEO\nIGUAL\nIGUAR\nIJADA\nIJIYO\nIJUJU\nILEON\nILEOS\nILESA\nILESO\nILION\nILOTA\nILUDA\nILUDE\nILUDI\nILUDO\nILUSA\nILUSO\nIMADA\nIMANA\nIMANE\nIMANO\nIMBUI\nIMELA\nIMITA\nIMITE\nIMITO\nIMPAR\nIMPIA\nIMPIO\nIMPLA\nIMPLE\nIMPLO\nIMPON\nINANE\nINCAS\nINCOA\nINCOE\nINCOO\nINDAS\nINDEX\nINDIA\nINDIO\nINDOS\nINFLA\nINFLE\nINFLO\nINGAS\nINGLE\nINGON\nINGRE\nINOPE\nINPUT\nINRIS\nINSTA\nINSTE\nINSTO\nINTER\nINTIS\nINTUI\nINVAR\nIONES\nIOTAS\nIPSIS\nIRADA\nIRADO\nIRANI\nIREIS\nIRGAN\nIRGAS\nIRGUE\nIRIAN\nIRIAS\nIRIDE\nIRISA\nIRISE\nIRISO\nIRRUI\nIRUPE\nISBAS\nISLAM\nISLAN\nISLAS\nISLEO\nISOCA\nISTMO\nITALA\nITALO\nITEMS\nITERA\nITERE\nITERO\nITRIA\nITRIO\nITZAJ\nIZABA\nIZADA\nIZADO\nIZAIS\nIZARA\nIZARE\nIZASE\nIZOTE\nJABAS\nJABIS\nJABLE\nJABON\nJABRA\nJABRE\nJABRI\nJABRO\nJACAL\nJACAS\nJACER\nJACHA\nJACOS\nJACTA\nJACTE\nJACTO\nJADAS\nJADEA\nJADEE\nJADEO\nJADES\nJADIA\nJADIE\nJADIO\nJAECE\nJAEZA\nJAEZO\nJAGUA\nJAIBA\nJAIMA\nJAJAY\nJALAD\nJALAN\nJALAR\nJALAS\nJALDA\nJALDE\nJALDO\nJALEA\nJALEE\nJALEN\nJALEO\nJALES\nJALMA\nJALON\nJAMAD\nJAMAN\nJAMAR\nJAMAS\nJAMBA\nJAMBE\nJAMBO\nJAMEN\nJAMES\nJAMON\nJANES\nJAPON\nJAQUE\nJARAL\nJARAS\nJARBA\nJARBE\nJARBO\nJARCA\nJARDA\nJAROS\nJARRA\nJARRE\nJARRO\nJASAD\nJASAN\nJASAR\nJASAS\nJASEN\nJASES\nJASPE\nJATAS\nJATEO\nJATES\nJATIB\nJATOS\nJAUDA\nJAUDO\nJAUJA\nJAULA\nJAUTA\nJAUTO\nJAVAS\nJAVOS\nJAYAN\nJANAS\nJANOS\nJEBES\nJEDAD\nJEDAN\nJEDAR\nJEDAS\nJEDEN\nJEDES\nJEFAS\nJEFES\nJEITO\nJEJEN\nJELIZ\nJEMAL\nJEMES\nJEQUE\nJERAS\nJERBO\nJEREZ\nJERGA\nJERPA\nJETAD\nJETAN\nJETAR\nJETAS\nJETEA\nJETEE\nJETEN\nJETEO\nJETES\nJETON\nJETOS\nJIBES\nJIBIA\nJICOS\nJIFAS\nJIFIA\nJIGAS\nJIGUE\nJIJAS\nJIJEA\nJIJEE\nJIJEO\nJIMAD\nJIMAN\nJIMAR\nJIMAS\nJIMEN\nJIMES\nJIMIA\nJIMIO\nJINDA\nJINES\nJIOTE\nJIPAS\nJIPIA\nJIPIE\nJIPIO\nJIPIS\nJIRAS\nJIREL\nJIRON\nJISCA\nJITAD\nJITAN\nJITAR\nJITAS\nJITEN\nJITES\nJINAD\nJINAN\nJINAR\nJINAS\nJINEN\nJOBAR\nJOBOS\nJOCHA\nJOCHE\nJOCHO\nJOCON\nJOCOS\nJODAN\nJODAS\nJODED\nJODEN\nJODER\nJODES\nJODIA\nJODIO\nJODON\nJOFOR\nJOLIN\nJONDO\nJONIA\nJONIO\nJOPAN\nJOPAR\nJOPAS\nJOPEA\nJOPEE\nJOPEN\nJOPEO\nJOPES\nJOPOS\nJORAS\nJORCO\nJORFE\nJORGA\nJORGE\nJORRO\nJOSAS\nJOTAS\nJOTES\nJOTOS\nJOULE\nJOVEN\nJOYAS\nJOYEL\nJOYON\nJOYOS\nJUANA\nJUBAS\nJUBON\nJUBOS\nJUCAS\nJUCOS\nJUDAS\nJUDIA\nJUDIO\nJUDOS\nJUEGA\nJUEGO\nJUERA\nJUEZA\nJUGAD\nJUGAR\nJUGAS\nJUGOS\nJUGUE\nJUJEA\nJUJEE\nJUJEO\nJULIA\nJULIO\nJULOS\nJUMAN\nJUMAR\nJUMAS\nJUMEA\nJUMEE\nJUMEN\nJUMEO\nJUMES\nJUMIL\nJUMOS\nJUNCE\nJUNCI\nJUNCO\nJUNIO\nJUNTA\nJUNTE\nJUNTO\nJUNZA\nJUNZO\nJUPAS\nJUPEA\nJUPEE\nJUPEO\nJUPON\nJURAD\nJURAN\nJURAR\nJURAS\nJURCO\nJUREL\nJUREN\nJURES\nJUROS\nJUSIS\nJUSTA\nJUSTE\nJUSTO\nJUTAS\nJUTIA\nJUVIA\nJUZGA\nJUZGO\nJUNAN\nJUNAS\nJUNEN\nJUNES\nJUNIA\nJUNID\nJUNIR\nJUNIS\nLABEO\nLABES\nLABIA\nLABIL\nLABIO\nLABOR\nLABRA\nLABRE\nLABRO\nLACAD\nLACAN\nLACAR\nLACAS\nLACEA\nLACEE\nLACEN\nLACEO\nLACES\nLACHA\nLACHO\nLACIA\nLACIO\nLACON\nLACRA\nLACRE\nLACRO\nLACTA\nLACTE\nLACTO\nLADAS\nLADEA\nLADEE\nLADEO\nLADON\nLADOS\nLADRA\nLADRE\nLADRO\nLAGAR\nLAGOS\nLAGUA\nLAICA\nLAICO\nLAIDA\nLAIDO\nLAJAS\nLAMAN\nLAMAS\nLAMBA\nLAMBE\nLAMBI\nLAMBO\nLAMED\nLAMEN\nLAMER\nLAMES\nLAMIA\nLAMIN\nLAMIO\nLAMPA\nLAMPE\nLAMPO\nLANAR\nLANAS\nLANCE\nLANDA\nLANDE\nLANDO\nLANGA\nLANIA\nLANIO\nLANZA\nLANZO\nLAPAS\nLAPIZ\nLAPON\nLAPOS\nLAPSA\nLAPSO\nLAQUE\nLARDA\nLARDE\nLARDO\nLARES\nLARGA\nLARGO\nLARRA\nLARVA\nLASAR\nLASAS\nLASCA\nLASCO\nLASER\nLASOS\nLASTA\nLASTE\nLASTO\nLASUN\nLATAN\nLATAS\nLATAZ\nLATEA\nLATEE\nLATEN\nLATEO\nLATES\nLATEX\nLATIA\nLATID\nLATIN\nLATIO\nLATIR\nLATIS\nLATON\nLATOS\nLAUDA\nLAUDE\nLAUDO\nLAUNA\nLAURO\nLAUTA\nLAUTO\nLAVAD\nLAVAN\nLAVAR\nLAVAS\nLAVEN\nLAVES\nLAXAD\nLAXAN\nLAXAR\nLAXAS\nLAXEN\nLAXES\nLAXOS\nLAYAD\nLAYAN\nLAYAR\nLAYAS\nLAYEN\nLAYES\nLAZAD\nLAZAN\nLAZAR\nLAZAS\nLAZOS\nLANAD\nLANAN\nLANEN\nLANES\nLEAIS\nLECHA\nLECHE\nLECHO\nLECOS\nLEDAS\nLEDON\nLEDOS\nLEEIS\nLEERA\nLEERE\nLEGAD\nLEGAL\nLEGAN\nLEGAR\nLEGAS\nLEGON\nLEGOS\nLEGRA\nLEGRE\nLEGRO\nLEGUA\nLEGUE\nLEGUI\nLEIAN\nLEIAS\nLEIDA\nLEIDO\nLEILA\nLEIMA\nLEJAS\nLEJIA\nLEJIO\nLEJOS\nLELAS\nLELOS\nLEMAN\nLEMAS\nLEMBO\nLEMPO\nLEMUR\nLENAS\nLENCA\nLENES\nLENON\nLENTA\nLENTE\nLENTO\nLEONA\nLEPRA\nLERAS\nLERDA\nLERDO\nLESAS\nLESEA\nLESEE\nLESEO\nLESNA\nLESOS\nLESTE\nLETAL\nLETEA\nLETEO\nLETON\nLETRA\nLEUCO\nLEUDA\nLEUDE\nLEUDO\nLEVAD\nLEVAN\nLEVAR\nLEVAS\nLEVEN\nLEVES\nLEYES\nLEZDA\nLEZNA\nLEZNE\nLENAD\nLENAN\nLENAR\nLENEN\nLENOS\nLIABA\nLIADA\nLIADO\nLIAIS\nLIANA\nLIARA\nLIARE\nLIASE\nLIAZA\nLIBAD\nLIBAN\nLIBAR\nLIBAS\nLIBEN\nLIBER\nLIBES\nLIBIA\nLIBIO\nLIBON\nLIBRA\nLIBRE\nLIBRO\nLICEO\nLICIA\nLICIO\nLICOR\nLICUA\nLICUE\nLICUO\nLIDER\nLIDES\nLIDIA\nLIDIE\nLIDIO\nLIDON\nLIEGA\nLIEGO\nLIEIS\nLIEVA\nLIEVE\nLIGAD\nLIGAN\nLIGAR\nLIGAS\nLIGHT\nLIGIO\nLIGON\nLIGUE\nLIGUR\nLIJAD\nLIJAN\nLIJAR\nLIJAS\nLIJEN\nLIJES\nLILAC\nLILAO\nLILAS\nLILIO\nLILOS\nLIMAD\nLIMAN\nLIMAR\nLIMAS\nLIMBO\nLIMEN\nLIMES\nLIMON\nLIMOS\nLINAO\nLINAR\nLINCE\nLINDA\nLINDE\nLINDO\nLINEA\nLINEE\nLINEO\nLINFA\nLINIO\nLINON\nLINOS\nLIOSA\nLIOSO\nLIPAS\nLIPES\nLIPIS\nLIPON\nLIRAS\nLIRIA\nLIRIO\nLIRON\nLISAS\nLISES\nLISIA\nLISIE\nLISIO\nLISIS\nLISOL\nLISOS\nLISTA\nLISTE\nLISTO\nLITAD\nLITAN\nLITAR\nLITAS\nLITEN\nLITES\nLITIO\nLITIS\nLITRE\nLITRO\nLITUO\nLIUDA\nLIUDE\nLIUDO\nLIVOR\nLIZAS\nLIZOS\nLLACA\nLLAGA\nLLAGO\nLLAMA\nLLAME\nLLAMO\nLLANA\nLLANO\nLLAPA\nLLAPE\nLLAPO\nLLAVE\nLLECA\nLLECO\nLLEGA\nLLEGO\nLLENA\nLLENE\nLLENO\nLLERA\nLLEVA\nLLEVE\nLLEVO\nLLORA\nLLORE\nLLORO\nLLOSA\nLLOVE\nLLOVI\nLOABA\nLOADA\nLOADO\nLOAIS\nLOARA\nLOARE\nLOASE\nLOBAS\nLOBBY\nLOBEA\nLOBEE\nLOBEO\nLOBOS\nLOCAL\nLOCAS\nLOCEA\nLOCEE\nLOCEO\nLOCHA\nLOCHE\nLOCOS\nLOCRO\nLODON\nLODOS\nLODRA\nLOEIS\nLOGAR\nLOGIA\nLOGIS\nLOGOS\nLOGRA\nLOGRE\nLOGRO\nLOICA\nLOINA\nLOINO\nLOLAS\nLOLEA\nLOLEE\nLOLEO\nLOLIO\nLOLIS\nLOLOS\nLOMAS\nLOMBA\nLOMBO\nLOMEA\nLOMEE\nLOMEO\nLOMOS\nLONAS\nLONCO\nLONGA\nLONGO\nLONJA\nLORAS\nLOREA\nLOREE\nLOREO\nLORES\nLOROS\nLORZA\nLOSAD\nLOSAN\nLOSAR\nLOSAS\nLOSEN\nLOSES\nLOTAS\nLOTEA\nLOTEE\nLOTEO\nLOTES\nLOTIN\nLOTOS\nLOZAS\nLUCAS\nLUCEN\nLUCES\nLUCHA\nLUCHE\nLUCHO\nLUCIA\nLUCID\nLUCIO\nLUCIR\nLUCIS\nLUCRA\nLUCRE\nLUCRO\nLUDAN\nLUDAS\nLUDEN\nLUDES\nLUDIA\nLUDID\nLUDIE\nLUDIO\nLUDIR\nLUDIS\nLUDOS\nLUEGO\nLUENE\nLUGAR\nLUGRE\nLUIAN\nLUIAS\nLUIDA\nLUIDO\nLUIRA\nLUIRE\nLUISA\nLUJAD\nLUJAN\nLUJAR\nLUJAS\nLUJEN\nLUJES\nLUJOS\nLULOS\nLULUS\nLUMAS\nLUMBO\nLUMEN\nLUMIA\nLUNAR\nLUNAS\nLUNCH\nLUNEA\nLUNEE\nLUNEL\nLUNEO\nLUNES\nLUNFA\nLUPAS\nLUPIA\nLUPUS\nLURTE\nLUSAS\nLUSCA\nLUSCO\nLUSOS\nLUTEA\nLUTEO\nLUTOS\nLUVIA\nLUXAD\nLUXAN\nLUXAR\nLUXAS\nLUXEN\nLUXES\nLUYAN\nLUYAS\nLUYEN\nLUYES\nLUZCA\nLUZCO\nLYCRA\nMABIS\nMABLE\nMACAL\nMACAN\nMACAR\nMACAS\nMACEA\nMACEE\nMACEN\nMACEO\nMACES\nMACHA\nMACHE\nMACHI\nMACHO\nMACIA\nMACIO\nMACIS\nMACLA\nMACON\nMACRO\nMACUA\nMADOR\nMADRE\nMAESA\nMAESE\nMAESO\nMAENA\nMAENO\nMAFIA\nMAGAS\nMAGIA\nMAGIE\nMAGIN\nMAGIO\nMAGMA\nMAGNA\nMAGNO\nMAGOS\nMAGRA\nMAGRO\nMAGUE\nMAHON\nMAIDO\nMAJAD\nMAJAL\nMAJAN\nMAJAR\nMAJAS\nMAJEA\nMAJEE\nMAJEN\nMAJEO\nMAJES\nMAJOS\nMALAR\nMALAS\nMALEA\nMALEE\nMALEO\nMALES\nMALIS\nMALLA\nMALLE\nMALLO\nMALON\nMALOS\nMALTA\nMALVA\nMALVE\nMALVO\nMAMAD\nMAMAN\nMAMAR\nMAMAS\nMAMBI\nMAMBO\nMAMEN\nMAMES\nMAMEY\nMAMIA\nMAMON\nMAMUA\nMAMUT\nMANAD\nMANAL\nMANAN\nMANAR\nMANAS\nMANCA\nMANCO\nMANDA\nMANDE\nMANDI\nMANDO\nMANEA\nMANEE\nMANEN\nMANEO\nMANES\nMANGA\nMANGO\nMANIA\nMANID\nMANIO\nMANIR\nMANIS\nMANOS\nMANSA\nMANSO\nMANTA\nMANTO\nMANUS\nMAOMA\nMAORI\nMAPAS\nMAPEA\nMAPEE\nMAPEO\nMAPOS\nMAQUE\nMAQUI\nMARAS\nMARCA\nMARCE\nMARCI\nMARCO\nMAREA\nMAREE\nMAREO\nMARES\nMARGA\nMARGO\nMARIA\nMARLO\nMARON\nMAROS\nMARRA\nMARRE\nMARRO\nMARSA\nMARSO\nMARTA\nMARTE\nMARZA\nMARZO\nMASAD\nMASAN\nMASAR\nMASAS\nMASCA\nMASCO\nMASEA\nMASEE\nMASEN\nMASEO\nMASES\nMASIA\nMASLO\nMASON\nMASTE\nMASTO\nMATAD\nMATAN\nMATAR\nMATAS\nMATEA\nMATEE\nMATEN\nMATEO\nMATES\nMATIZ\nMATON\nMATOS\nMATUL\nMAULA\nMAULE\nMAULO\nMAURA\nMAURE\nMAURO\nMAYAD\nMAYAL\nMAYAN\nMAYAR\nMAYAS\nMAYEA\nMAYEE\nMAYEN\nMAYEO\nMAYES\nMAYOR\nMAYOS\nMAZAD\nMAZAN\nMAZAR\nMAZAS\nMAZNA\nMAZNE\nMAZNO\nMAZOS\nMBAYA\nMEABA\nMEADA\nMEADO\nMEAIS\nMEAJA\nMEANO\nMEARA\nMEARE\nMEASE\nMEATO\nMECAS\nMECED\nMECEN\nMECER\nMECES\nMECHA\nMECHE\nMECHO\nMECIA\nMECIO\nMECOS\nMEDAS\nMEDIA\nMEDID\nMEDIE\nMEDIO\nMEDIR\nMEDIS\nMEDOS\nMEDRA\nMEDRE\nMEDRO\nMEEIS\nMEGAS\nMEGOS\nMEIGA\nMEIGO\nMEJAN\nMEJAS\nMEJED\nMEJEN\nMEJER\nMEJES\nMEJIA\nMEJIO\nMEJOR\nMELAD\nMELAR\nMELAS\nMELCA\nMELGA\nMELGO\nMELIS\nMELLA\nMELLE\nMELLO\nMELON\nMELSA\nMELVA\nMEMAS\nMEMEZ\nMEMOS\nMENAD\nMENAN\nMENAR\nMENAS\nMENDA\nMENEA\nMENEE\nMENEN\nMENEO\nMENES\nMENGE\nMENOR\nMENOS\nMENSA\nMENSO\nMENSU\nMENTA\nMENTE\nMENTI\nMENTO\nMENUS\nMEONA\nMERAD\nMERAN\nMERAR\nMERAS\nMERCA\nMERCO\nMEREN\nMERES\nMEREY\nMERGO\nMERLA\nMERLO\nMERMA\nMERME\nMERMO\nMEROL\nMEROS\nMERSA\nMESAD\nMESAN\nMESAR\nMESAS\nMESEN\nMESES\nMESMA\nMESMO\nMESON\nMESTA\nMESTO\nMETAD\nMETAL\nMETAN\nMETAS\nMETED\nMETEN\nMETER\nMETES\nMETIA\nMETIO\nMETRA\nMETRO\nMEYAS\nMEYOR\nMEZAN\nMEZAS\nMIABA\nMIADO\nMIAGA\nMIAGO\nMIAIS\nMIAJA\nMIARA\nMIARE\nMIASE\nMIAUS\nMIANA\nMIANE\nMIANO\nMICAS\nMICER\nMICES\nMICHA\nMICHE\nMICHO\nMICOS\nMICRA\nMICRO\nMIDAN\nMIDAS\nMIDEN\nMIDES\nMIDIO\nMIEDO\nMIEIS\nMIELA\nMIELE\nMIELO\nMIERA\nMIGAD\nMIGAN\nMIGAR\nMIGAS\nMIGRA\nMIGRE\nMIGRO\nMIGUE\nMIJOS\nMILAN\nMILES\nMILIS\nMILLA\nMILLO\nMILPA\nMIMAD\nMIMAN\nMIMAR\nMIMAS\nMIMEN\nMIMES\nMIMOS\nMINAD\nMINAL\nMINAN\nMINAR\nMINAS\nMINAZ\nMINEN\nMINES\nMINGA\nMINGO\nMINIA\nMINIE\nMINIO\nMINUE\nMIOMA\nMIONA\nMIOPE\nMIRAD\nMIRAN\nMIRAR\nMIRAS\nMIREN\nMIRES\nMIRLA\nMIRLE\nMIRLO\nMIRON\nMIRRA\nMIRTO\nMIRZA\nMISAD\nMISAL\nMISAN\nMISAR\nMISAS\nMISEN\nMISES\nMISIA\nMISIL\nMISIO\nMISMA\nMISMO\nMISTA\nMISTE\nMISTO\nMITAD\nMITAN\nMITAS\nMITIN\nMITON\nMITOS\nMITRA\nMITRE\nMITRO\nMIURA\nMIXTA\nMIXTO\nMIZAS\nMIZOS\nMINON\nMOAIS\nMOARE\nMOBLE\nMOCAD\nMOCAN\nMOCAR\nMOCAS\nMOCEA\nMOCEE\nMOCEO\nMOCHA\nMOCHE\nMOCHO\nMOCIL\nMOCOS\nMODAL\nMODAS\nMODEM\nMODIO\nMODOS\nMOFAD\nMOFAN\nMOFAR\nMOFAS\nMOFEN\nMOFES\nMOGAS\nMOGOL\nMOGON\nMOGOS\nMOHIN\nMOHOS\nMOHUR\nMOJAD\nMOJAN\nMOJAR\nMOJAS\nMOJEL\nMOJEN\nMOJES\nMOJIL\nMOJIS\nMOJON\nMOJOS\nMOLAD\nMOLAN\nMOLAR\nMOLAS\nMOLDA\nMOLDE\nMOLDO\nMOLED\nMOLEN\nMOLER\nMOLES\nMOLIA\nMOLIO\nMOLLA\nMOLLE\nMOLON\nMOLOS\nMOLSA\nMOLSO\nMOMEA\nMOMEE\nMOMEO\nMOMIA\nMOMIO\nMOMOS\nMONAS\nMONDA\nMONDE\nMONDO\nMONEA\nMONEE\nMONEO\nMONFI\nMONGA\nMONGO\nMONIS\nMONJA\nMONJE\nMONOS\nMONRA\nMONSE\nMONTA\nMONTE\nMONTO\nMOPAN\nMOPAS\nMOQUE\nMORAD\nMORAL\nMORAN\nMORAR\nMORAS\nMORBO\nMORCA\nMORCO\nMORDE\nMORDI\nMOREA\nMOREN\nMOREO\nMORES\nMORFA\nMORFE\nMORFO\nMORGA\nMORIA\nMORID\nMORIR\nMORIS\nMORMA\nMORME\nMORMO\nMORON\nMOROS\nMORRA\nMORRO\nMORSA\nMORSE\nMOSCA\nMOSCO\nMOSEN\nMOSTE\nMOSTO\nMOTAS\nMOTEA\nMOTEE\nMOTEL\nMOTEO\nMOTES\nMOTIL\nMOTIN\nMOTON\nMOTOR\nMOTOS\nMOVED\nMOVER\nMOVES\nMOVIA\nMOVIL\nMOVIO\nMOXAS\nMOXTE\nMOYAS\nMOYOS\nMOZAS\nMOZOS\nMONON\nMUARE\nMUBLE\nMUCAS\nMUCHA\nMUCHO\nMUCOS\nMUDAD\nMUDAN\nMUDAR\nMUDAS\nMUDEN\nMUDES\nMUDEZ\nMUDOS\nMUECA\nMUELA\nMUELE\nMUELO\nMUERA\nMUERE\nMUERO\nMUESO\nMUEVA\nMUEVE\nMUEVO\nMUFAS\nMUFLA\nMUFTI\nMUGAD\nMUGAN\nMUGAR\nMUGAS\nMUGEN\nMUGES\nMUGIA\nMUGID\nMUGIL\nMUGIO\nMUGIR\nMUGIS\nMUGLE\nMUGOR\nMUGRE\nMUGUE\nMUIAN\nMUIAS\nMUIDA\nMUIDO\nMUIRA\nMUIRE\nMUJAN\nMUJAS\nMUJER\nMUJOL\nMULAR\nMULAS\nMULEO\nMULES\nMULLA\nMULLE\nMULLI\nMULLO\nMULOS\nMULSA\nMULSO\nMULTA\nMULTE\nMULTO\nMUNAS\nMUNDO\nMURAD\nMURAL\nMURAN\nMURAR\nMURAS\nMUREN\nMURES\nMURGA\nMURIA\nMURIO\nMUROS\nMURTA\nMURTO\nMUSAN\nMUSAR\nMUSAS\nMUSCA\nMUSCO\nMUSEN\nMUSEO\nMUSES\nMUSGA\nMUSGO\nMUSIA\nMUSIO\nMUSIR\nMUSIS\nMUSLO\nMUSOS\nMUTAD\nMUTAN\nMUTAR\nMUTAS\nMUTEN\nMUTES\nMUTIS\nMUTRA\nMUTRO\nMUTUA\nMUTUO\nMUYAN\nMUYAS\nMUYEN\nMUYES\nMUNAN\nMUNEN\nMUNES\nMUNIA\nMUNID\nMUNIR\nMUNIS\nMUNON\nNABAB\nNABAL\nNABAR\nNABAS\nNABIS\nNABLA\nNABOS\nNACAR\nNACAS\nNACED\nNACEN\nNACER\nNACES\nNACHA\nNACHO\nNACIA\nNACIO\nNACOS\nNACRE\nNADAD\nNADAL\nNADAN\nNADAR\nNADAS\nNADEN\nNADES\nNADGA\nNADIE\nNADIR\nNAFRA\nNAFRE\nNAFRO\nNAFTA\nNAGUA\nNAHOA\nNAHUA\nNAIFE\nNAIFS\nNAIPE\nNAIRE\nNAJAS\nNALCA\nNALGA\nNANAS\nNANAY\nNANCE\nNANEA\nNANEE\nNANEO\nNANSA\nNANSU\nNANTA\nNANTE\nNANTO\nNAPAS\nNAPEA\nNAPEO\nNAQUE\nNARCO\nNARDO\nNARES\nNARIZ\nNARRA\nNARRE\nNARRO\nNASAL\nNASAS\nNASON\nNASOS\nNATAL\nNATAS\nNATIA\nNATIO\nNATOS\nNATRI\nNAUTA\nNAVAL\nNAVAS\nNAVES\nNAVIO\nNAZCA\nNAZCO\nNAZIS\nNEBEL\nNEBIS\nNEBLI\nNEBRO\nNECEA\nNECEE\nNECEO\nNECIA\nNECIO\nNEGAD\nNEGAR\nNEGAS\nNEGRA\nNEGRO\nNEGUE\nNEGUS\nNEJAS\nNEJOS\nNELDO\nNELES\nNEMAS\nNEMEA\nNEMEO\nNEMES\nNEMON\nNENAS\nNENES\nNENIA\nNEPES\nNERON\nNESGA\nNESGO\nNETAS\nNETOS\nNEUMA\nNEVAD\nNEVAR\nNEVAS\nNEVOS\nNEVUS\nNEXOS\nNIARA\nNIAZO\nNICHE\nNICHO\nNICLE\nNICOL\nNIDAL\nNIDIA\nNIDIO\nNIDOS\nNIEGA\nNIEGO\nNIELA\nNIELE\nNIELO\nNIETA\nNIETO\nNIEVA\nNIEVE\nNIEVO\nNIGUA\nNILAD\nNILON\nNIMBA\nNIMBE\nNIMBO\nNIMIA\nNIMIO\nNINFA\nNINFO\nNINOT\nNIOTO\nNIPAS\nNIPIS\nNIPON\nNIPOS\nNIQUI\nNISTE\nNITOR\nNITOS\nNITRA\nNITRE\nNITRO\nNIVEA\nNIVEL\nNIVEO\nNIXTE\nNINAS\nNINEA\nNINEE\nNINEO\nNINEZ\nNINOS\nNOBEL\nNOBLE\nNOCAS\nNOCHE\nNOCIR\nNOCLA\nNODAL\nNODOS\nNOEMA\nNOGAL\nNOLIS\nNOLIT\nNOMAS\nNOMON\nNOMOS\nNONAS\nNONES\nNONIO\nNONOS\nNOPAL\nNOQUE\nNORAY\nNORIA\nNORMA\nNORME\nNORMO\nNORTE\nNOTAD\nNOTAN\nNOTAR\nNOTAS\nNOTEN\nNOTES\nNOTOS\nNOTRO\nNOVAD\nNOVAL\nNOVAN\nNOVAR\nNOVAS\nNOVEL\nNOVEN\nNOVES\nNOVIA\nNOVIE\nNOVIO\nNOYOS\nNUBES\nNUBIA\nNUBIL\nNUBIO\nNUBLA\nNUBLE\nNUBLO\nNUCAS\nNUCHE\nNUCIR\nNUCOS\nNUDAS\nNUDOS\nNUERA\nNUESA\nNUESO\nNUEVA\nNUEVE\nNUEVO\nNUEZA\nNULAS\nNULOS\nNUMEN\nNUMOS\nNUNCA\nNUTRA\nNUTRE\nNUTRI\nNUTRO\nNUNOS\nOASIS\nOBELO\nOBESA\nOBESO\nOBICE\nOBITO\nOBLEA\nOBOES\nOBOLO\nOBRAD\nOBRAN\nOBRAR\nOBRAS\nOBREN\nOBRES\nOBSTA\nOBSTE\nOBSTO\nOBTEN\nOBUES\nOBVIA\nOBVIE\nOBVIO\nOCAPI\nOCASO\nOCELO\nOCENA\nOCHOS\nOCIAD\nOCIAN\nOCIAR\nOCIAS\nOCIEN\nOCIES\nOCIOS\nOCLES\nOCLUI\nOCOTE\nOCRAS\nOCRES\nOCREY\nOCUJE\nOCUME\nOCUMO\nOCUPA\nOCUPE\nOCUPO\nODEON\nODIAD\nODIAN\nODIAR\nODIAS\nODIEN\nODIES\nODIOS\nODRES\nOESTE\nOFITA\nOGANO\nOGROS\nOHMIO\nOIAIS\nOIBLE\nOIDAS\nOIDIO\nOIDOR\nOIDOS\nOIGAN\nOIGAS\nOIMOS\nOIRAN\nOIRAS\nOIRIA\nOISLO\nOISTE\nOJALA\nOJALE\nOJALO\nOJEAD\nOJEAN\nOJEAR\nOJEAS\nOJEEN\nOJEES\nOJEOS\nOJERA\nOJETE\nOJITO\nOJIVA\nOJOSA\nOJOSO\nOJOTA\nOJUDA\nOJUDO\nOLAIS\nOLAJE\nOLEAD\nOLEAN\nOLEAR\nOLEAS\nOLEEN\nOLEES\nOLEIS\nOLEOS\nOLERA\nOLERE\nOLIAN\nOLIAS\nOLIDA\nOLIDO\nOLIOS\nOLIVA\nOLIVE\nOLIVO\nOLLAO\nOLLAR\nOLLAS\nOLMAS\nOLMOS\nOLOTE\nOLURA\nOMANI\nOMASO\nOMBUS\nOMEGA\nOMERO\nOMEYA\nOMINA\nOMINE\nOMINO\nOMISA\nOMISO\nOMITA\nOMITE\nOMITI\nOMITO\nOMOTO\nONCEA\nONCEE\nONCEO\nONCES\nONDAS\nONDEA\nONDEE\nONDEO\nONDRA\nONECE\nONECI\nONICE\nONOTO\nONZAS\nOPACA\nOPACO\nOPADA\nOPADO\nOPALO\nOPERA\nOPERE\nOPERO\nOPILA\nOPILE\nOPILO\nOPIMA\nOPIMO\nOPINA\nOPINE\nOPINO\nOPIOS\nOPONE\nOPTAD\nOPTAN\nOPTAR\nOPTAS\nOPTEN\nOPTES\nOPUSE\nOPUSO\nORABA\nORADA\nORADO\nORAIS\nORAJE\nORALE\nORARA\nORARE\nORASE\nORATE\nORBES\nORCAS\nORCEN\nORCES\nORCOS\nORDEN\nOREAD\nOREAN\nOREAR\nOREAS\nOREEN\nOREES\nOREIS\nOREJA\nOREOS\nORERO\nORFOS\nORFRE\nORGIA\nORIBE\nORIES\nORINA\nORINE\nORINO\nORIOL\nORIVE\nORLAD\nORLAN\nORLAR\nORLAS\nORLEN\nORLES\nORLOS\nORNAD\nORNAN\nORNAR\nORNAS\nORNEA\nORNEE\nORNEN\nORNEO\nORNES\nOROYA\nORTOS\nORUGA\nORUJO\nORZAD\nORZAN\nORZAR\nORZAS\nOSABA\nOSADA\nOSADO\nOSAIS\nOSARA\nOSARE\nOSASE\nOSCAS\nOSCOS\nOSEAD\nOSEAN\nOSEAR\nOSEAS\nOSEEN\nOSEES\nOSEIS\nOSEOS\nOSERA\nOSERO\nOSETA\nOSMIO\nOSOSA\nOSOSO\nOSTAS\nOSTIA\nOSTRA\nOSTRO\nOSUDA\nOSUDO\nOSUNA\nOSUNO\nOTATE\nOTEAD\nOTEAN\nOTEAR\nOTEAS\nOTEEN\nOTEES\nOTERO\nOTILA\nOTILE\nOTILO\nOTOBA\nOTONA\nOTONE\nOTONO\nOTRAS\nOTRES\nOTRIS\nOTROS\nOVABA\nOVADA\nOVADO\nOVAIS\nOVALA\nOVALE\nOVALO\nOVARA\nOVARE\nOVASE\nOVEIS\nOVEJA\nOVERA\nOVERO\nOVIDO\nOVINA\nOVINO\nOVNIS\nOVOLO\nOVOSA\nOVOSO\nOVULA\nOVULE\nOVULO\nOXEAD\nOXEAN\nOXEAR\nOXEAS\nOXEEN\nOXEES\nOXIDA\nOXIDE\nOXIDO\nOYERA\nOYERE\nOYESE\nOZENA\nOZONA\nOZONO\nPACAE\nPACAS\nPACAY\nPACED\nPACEN\nPACER\nPACES\nPACHA\nPACHO\nPACIA\nPACIO\nPACON\nPACOS\nPACTA\nPACTE\nPACTO\nPACUS\nPADRE\nPAFIA\nPAFIO\nPAGAD\nPAGAN\nPAGAR\nPAGAS\nPAGEL\nPAGOS\nPAGRO\nPAGUA\nPAGUE\nPAHUA\nPAICO\nPAILA\nPAINA\nPAIRA\nPAIRE\nPAIRO\nPAJAR\nPAJAS\nPAJEA\nPAJEE\nPAJEL\nPAJEO\nPAJES\nPAJIL\nPAJLA\nPAJON\nPAJOS\nPAJUZ\nPALAS\nPALAY\nPALCA\nPALCO\nPALEA\nPALEE\nPALEO\nPALES\nPALIA\nPALIE\nPALIO\nPALIS\nPALLA\nPALLE\nPALLO\nPALMA\nPALME\nPALMO\nPALON\nPALOR\nPALOS\nPALPA\nPALPE\nPALPI\nPALPO\nPALTA\nPALTO\nPAMBA\nPAMPA\nPAMUE\nPANAL\nPANAS\nPANCA\nPANCO\nPANDA\nPANDO\nPANEL\nPANES\nPANGA\nPANJI\nPANOS\nPANSA\nPANTY\nPANUL\nPANZA\nPAPAD\nPAPAL\nPAPAN\nPAPAR\nPAPAS\nPAPAZ\nPAPEA\nPAPEE\nPAPEL\nPAPEN\nPAPEO\nPAPES\nPAPIN\nPAPON\nPAPOS\nPAPUA\nPAPUS\nPARAD\nPARAL\nPARAN\nPARAO\nPARAR\nPARAS\nPARCA\nPARCE\nPARCO\nPARDA\nPARDO\nPAREA\nPARED\nPAREE\nPAREL\nPAREN\nPAREO\nPARES\nPARGO\nPARIA\nPARID\nPARIO\nPARIR\nPARIS\nPARLA\nPARLE\nPARLO\nPARNE\nPAROS\nPARPA\nPARPE\nPARPO\nPARRA\nPARRE\nPARRO\nPARSI\nPARTA\nPARTE\nPARTI\nPARTO\nPARVA\nPARVO\nPASAD\nPASAN\nPASAR\nPASAS\nPASCO\nPASEA\nPASEE\nPASEN\nPASEO\nPASES\nPASIL\nPASMA\nPASME\nPASMO\nPASOS\nPASPA\nPASPE\nPASPO\nPASTA\nPASTE\nPASTO\nPATAN\nPATAO\nPATAS\nPATAX\nPATAY\nPATEA\nPATEE\nPATEO\nPATER\nPATES\nPATIN\nPATIO\nPATIS\nPATON\nPATOS\nPAUJI\nPAULA\nPAULE\nPAULO\nPAUSA\nPAUSE\nPAUSO\nPAUTA\nPAUTE\nPAUTO\nPAVAS\nPAVES\nPAVIA\nPAVON\nPAVOR\nPAVOS\nPAXTE\nPAYAD\nPAYAN\nPAYAR\nPAYAS\nPAYEN\nPAYES\nPAYOS\nPAZCA\nPAZCO\nPAZOS\nPANIL\nPANOL\nPEAIS\nPEAJE\nPEALA\nPEALE\nPEALO\nPEANA\nPEBRE\nPECAD\nPECAN\nPECAR\nPECAS\nPECES\nPECHA\nPECHE\nPECHO\nPECIO\nPECTA\nPECTE\nPECTO\nPEDAL\nPEDIA\nPEDID\nPEDIO\nPEDIR\nPEDIS\nPEDOS\nPEDRO\nPEEIS\nPEERA\nPEERE\nPEGAD\nPEGAN\nPEGAR\nPEGAS\nPEGON\nPEGOS\nPEGUE\nPEIAN\nPEIAS\nPEIDO\nPEINA\nPEINE\nPEINO\nPEJES\nPEJIN\nPELAD\nPELAN\nPELAR\nPELAS\nPELDE\nPELEA\nPELEE\nPELEN\nPELEO\nPELES\nPELIS\nPELLA\nPELLO\nPELMA\nPELON\nPELOS\nPELTA\nPELUS\nPELVI\nPEMON\nPENAD\nPENAL\nPENAN\nPENAR\nPENAS\nPENCA\nPENCO\nPENDA\nPENDE\nPENDI\nPENDO\nPENEN\nPENES\nPENIS\nPENOL\nPENOS\nPENSA\nPENSE\nPENSO\nPEORA\nPEPAS\nPEPES\nPEPLA\nPEPLO\nPEPON\nPEPUS\nPEQUE\nPERAL\nPERAS\nPERCA\nPERDE\nPERDI\nPERIS\nPERLA\nPERLE\nPERLO\nPERNA\nPERNO\nPEROL\nPEROS\nPERRA\nPERRO\nPERSA\nPERTA\nPERUS\nPESAD\nPESAN\nPESAR\nPESAS\nPESCA\nPESCE\nPESCO\nPESEN\nPESES\nPESGA\nPESGO\nPESIA\nPESOL\nPESOR\nPESOS\nPESTE\nPETAD\nPETAN\nPETAR\nPETAS\nPETEN\nPETES\nPETOS\nPETRA\nPEUCO\nPEUMO\nPEZON\nPENON\nPIABA\nPIADA\nPIADO\nPIAFA\nPIAFE\nPIAFO\nPIAIS\nPIALA\nPIALE\nPIALO\nPIANO\nPIARA\nPIARE\nPIASE\nPIBAS\nPIBES\nPIBIL\nPICAD\nPICAL\nPICAN\nPICAR\nPICAS\nPICEA\nPICEO\nPICHA\nPICHE\nPICHI\nPICHO\nPICON\nPICOR\nPICOS\nPICUY\nPIDAN\nPIDAS\nPIDEN\nPIDES\nPIDIO\nPIDON\nPIEIS\nPIEJO\nPIEZA\nPIFAS\nPIFIA\nPIFIE\nPIFIO\nPIGRA\nPIGRE\nPIGRO\nPIGUA\nPIHUA\nPIJAS\nPIJES\nPIJIN\nPIJOS\nPIJUL\nPIJUY\nPILAD\nPILAN\nPILAR\nPILAS\nPILCA\nPILEN\nPILEO\nPILES\nPILLA\nPILLE\nPILLO\nPILME\nPILON\nPILOS\nPINAL\nPINAR\nPINAS\nPINCE\nPINES\nPINGA\nPINGO\nPINNA\nPINOL\nPINOS\nPINTA\nPINTE\nPINTO\nPINZA\nPINZO\nPIOJO\nPIOLA\nPIOLE\nPIOLO\nPIONA\nPIPAD\nPIPAN\nPIPAR\nPIPAS\nPIPEN\nPIPES\nPIPIA\nPIPIE\nPIPIL\nPIPIO\nPIPIS\nPIPON\nPIPOS\nPIQUE\nPIRAD\nPIRAL\nPIRAN\nPIRAR\nPIRAS\nPIRCA\nPIRCO\nPIREN\nPIRES\nPIRLA\nPIRON\nPIROS\nPIRRA\nPIRRE\nPIRRI\nPIRRO\nPIRUL\nPIRUS\nPISAD\nPISAN\nPISAR\nPISAS\nPISCA\nPISCO\nPISEN\nPISES\nPISON\nPISOS\nPISPA\nPISPE\nPISPO\nPISTA\nPISTE\nPISTO\nPITAD\nPITAL\nPITAN\nPITAO\nPITAR\nPITAS\nPITEA\nPITEE\nPITEN\nPITEO\nPITES\nPITIA\nPITIO\nPITIS\nPITON\nPITOS\nPIULA\nPIULE\nPIULO\nPIUNE\nPIURE\nPIVOT\nPIXEL\nPIZCA\nPIZCO\nPIZZA\nPINEN\nPINON\nPLACA\nPLACE\nPLACI\nPLACO\nPLAGA\nPLAGO\nPLANA\nPLANO\nPLATA\nPLATO\nPLAYA\nPLAYE\nPLAYO\nPLAZA\nPLAZO\nPLANE\nPLANI\nPLEBE\nPLECA\nPLEGA\nPLEGO\nPLENA\nPLENO\nPLEON\nPLEPA\nPLEXO\nPLICA\nPLISA\nPLISE\nPLISO\nPLOMA\nPLOME\nPLOMO\nPLUGO\nPLUMA\nPOBLA\nPOBLE\nPOBLO\nPOBOS\nPOBRA\nPOBRE\nPOCAS\nPOCHA\nPOCHO\nPOCOS\nPODAD\nPODAL\nPODAN\nPODAR\nPODAS\nPODED\nPODEN\nPODER\nPODES\nPODIA\nPODIO\nPODON\nPODRA\nPODRE\nPOEMA\nPOETA\nPOINO\nPOISA\nPOISE\nPOLAR\nPOLCA\nPOLCO\nPOLEA\nPOLEN\nPOLEO\nPOLEX\nPOLIN\nPOLIO\nPOLIR\nPOLIS\nPOLLA\nPOLLO\nPOLOS\nPOLVO\nPOMAR\nPOMAS\nPOMEZ\nPOMOS\nPOMPA\nPOMPO\nPONCI\nPONED\nPONEN\nPONER\nPONES\nPONEY\nPONGA\nPONGO\nPONIA\nPONIS\nPONTO\nPOPAD\nPOPAN\nPOPAR\nPOPAS\nPOPEL\nPOPEN\nPOPES\nPOPOS\nPOPTI\nPORCO\nPORGA\nPORGO\nPORNO\nPOROS\nPORRA\nPORRO\nPORTA\nPORTE\nPORTO\nPOSAD\nPOSAN\nPOSAR\nPOSAS\nPOSCA\nPOSEA\nPOSEE\nPOSEI\nPOSEN\nPOSEO\nPOSES\nPOSMA\nPOSMO\nPOSON\nPOSOS\nPOSTA\nPOSTE\nPOTAD\nPOTAN\nPOTAR\nPOTAS\nPOTEA\nPOTEE\nPOTEN\nPOTEO\nPOTES\nPOTOS\nPOTRA\nPOTRO\nPOYAD\nPOYAL\nPOYAN\nPOYAR\nPOYAS\nPOYEN\nPOYES\nPOYOS\nPOZAL\nPOZAS\nPOZOL\nPOZOS\nPRADO\nPRAOS\nPRAVA\nPRAVO\nPRAZA\nPREAR\nPREAS\nPREDA\nPRESA\nPRESO\nPREST\nPREVE\nPREVI\nPRENA\nPRENE\nPRENO\nPRIMA\nPRIME\nPRIMO\nPRION\nPRIOR\nPRISA\nPRIVA\nPRIVE\nPRIVO\nPROAL\nPROAS\nPROBA\nPROBE\nPROBO\nPROCO\nPROEL\nPROFA\nPROFE\nPROIS\nPROIZ\nPROLE\nPRONA\nPRONO\nPRORA\nPROSA\nPRUNA\nPRUNO\nPSIES\nPUABA\nPUADA\nPUADO\nPUAIS\nPUARA\nPUARE\nPUASE\nPUBER\nPUBES\nPUBIS\nPUCHA\nPUCHO\nPUCIA\nPUDIN\nPUDIO\nPUDIR\nPUDOR\nPUDRA\nPUDRE\nPUDRI\nPUDRO\nPUDUS\nPUEDA\nPUEDE\nPUEDO\nPUEIS\nPUFOS\nPUGAS\nPUGIL\nPUGNA\nPUGNE\nPUGNO\nPUJAD\nPUJAN\nPUJAR\nPUJAS\nPUJEN\nPUJES\nPUJOS\nPULAN\nPULAS\nPULEN\nPULES\nPULGA\nPULIA\nPULID\nPULIO\nPULIR\nPULIS\nPULLA\nPULLE\nPULLO\nPULPA\nPULPO\nPULSA\nPULSE\nPULSO\nPUMAS\nPUMBA\nPUNAN\nPUNAR\nPUNAS\nPUNCE\nPUNEN\nPUNES\nPUNGA\nPUNGE\nPUNGI\nPUNIA\nPUNID\nPUNIO\nPUNIR\nPUNIS\nPUNJA\nPUNJO\nPUNTA\nPUNTE\nPUNTO\nPUNZA\nPUNZO\nPUPAD\nPUPAN\nPUPAR\nPUPAS\nPUPEN\nPUPES\nPUPOS\nPUPUS\nPURAS\nPUREA\nPUREE\nPUREO\nPURES\nPURGA\nPURGO\nPURIN\nPUROS\nPURRA\nPURRE\nPURRI\nPURRO\nPUSES\nPUSPA\nPUSPO\nPUTAL\nPUTAS\nPUTEA\nPUTEE\nPUTEO\nPUTON\nPUTOS\nPUYAD\nPUYAN\nPUYAR\nPUYAS\nPUYEN\nPUYES\nPUYON\nPUYOS\nPUZLE\nPUZOL\nPUNAL\nPUNOS\nPYMES\nQUECO\nQUEDA\nQUEDE\nQUEDO\nQUEJA\nQUEJE\nQUEJO\nQUEMA\nQUEME\nQUEMI\nQUEMO\nQUENA\nQUEPA\nQUEPI\nQUEPO\nQUERA\nQUERE\nQUERO\nQUESO\nQUIAS\nQUIEN\nQUIER\nQUIFS\nQUIJO\nQUILA\nQUILO\nQUIMA\nQUIMO\nQUINA\nQUINO\nQUIOS\nQUIPA\nQUIPU\nQUISA\nQUISE\nQUISO\nQUITA\nQUITE\nQUITO\nQUITU\nQUIVI\nQUIZA\nQUINE\nRABAL\nRABAS\nRABEA\nRABEE\nRABEL\nRABEO\nRABIA\nRABIE\nRABIL\nRABIO\nRABIS\nRABON\nRABOS\nRACEA\nRACEE\nRACEL\nRACEO\nRACHA\nRACHE\nRACHO\nRACOR\nRACOS\nRADAL\nRADAR\nRADAS\nRADES\nRADIA\nRADIE\nRADIO\nRADON\nRAEIS\nRAERA\nRAERE\nRAFAL\nRAFAS\nRAFEA\nRAFEE\nRAFEO\nRAFES\nRAFEZ\nRAFIA\nRAGUA\nRAGUS\nRAHEZ\nRAIAN\nRAIAS\nRAICE\nRAIDA\nRAIDO\nRAIGA\nRAIGO\nRAIJO\nRAIZA\nRAIZO\nRAJAD\nRAJAN\nRAJAR\nRAJAS\nRAJEN\nRAJES\nRAJON\nRALAS\nRALBA\nRALBE\nRALBO\nRALEA\nRALEE\nRALEO\nRALLA\nRALLE\nRALLO\nRALLY\nRALOS\nRALVA\nRALVE\nRALVO\nRAMAL\nRAMAS\nRAMEA\nRAMEE\nRAMEO\nRAMIO\nRAMON\nRAMOS\nRAMPA\nRAMPE\nRAMPO\nRANAS\nRANDA\nRANDS\nRANGO\nRANOS\nRAPAD\nRAPAN\nRAPAR\nRAPAS\nRAPAZ\nRAPEN\nRAPES\nRAPOS\nRAPTA\nRAPTE\nRAPTO\nRAQUE\nRARAS\nRAREA\nRAREE\nRAREO\nRAROS\nRASAD\nRASAN\nRASAR\nRASAS\nRASCA\nRASCO\nRASEL\nRASEN\nRASES\nRASGA\nRASGO\nRASIS\nRASOS\nRASPA\nRASPE\nRASPO\nRATAS\nRATEA\nRATEE\nRATEO\nRATIO\nRATON\nRATOS\nRAUCA\nRAUCO\nRAUDA\nRAUDO\nRAULI\nRAUTA\nRAYAD\nRAYAN\nRAYAR\nRAYAS\nRAYEN\nRAYES\nRAYON\nRAYOS\nRAZAR\nRAZAS\nRAZIA\nRAZON\nREAJE\nREALA\nREAMA\nREAME\nREAMO\nREARA\nREARE\nREARO\nREATA\nREATE\nREATO\nREBLA\nREBLE\nREBLO\nREBOL\nREBUS\nRECAE\nRECAI\nRECEL\nRECEN\nRECES\nRECIA\nRECIO\nRECLE\nRECRE\nRECTA\nRECTE\nRECTO\nRECUA\nREDAD\nREDAN\nREDAR\nREDAS\nREDEL\nREDEN\nREDES\nREDIL\nREDOL\nREDOR\nREDRO\nREFEZ\nREGAD\nREGAR\nREGAS\nREGIA\nREGID\nREGIO\nREGIR\nREGIS\nREGLA\nREGLE\nREGLO\nREGUE\nREHAZ\nREHEN\nREHUI\nREHUS\nREIAN\nREIAS\nREIDA\nREIDO\nREILA\nREILE\nREILO\nREINA\nREINE\nREINO\nREIRA\nREIRE\nREJAL\nREJAS\nREJIN\nREJON\nREJOS\nREJUS\nRELAX\nRELEA\nRELEE\nRELEI\nRELEJ\nRELEO\nRELES\nRELOJ\nRELSA\nRELSO\nRELVA\nRELVE\nRELVO\nREMAD\nREMAN\nREMAR\nREMAS\nREMEN\nREMES\nREMOS\nRENAL\nRENCA\nRENCO\nRENDA\nRENDE\nRENDI\nRENDO\nRENES\nRENGA\nRENGO\nRENIL\nRENIO\nRENOS\nRENTA\nRENTE\nRENTO\nREOCA\nREOJO\nREPON\nREPOS\nREPTA\nREPTE\nREPTO\nRESAL\nRESES\nRESMA\nRESOL\nRESPE\nRESTA\nRESTE\nRESTO\nRETAD\nRETAL\nRETAN\nRETAR\nRETAS\nRETEL\nRETEN\nRETES\nRETIN\nRETOR\nRETOS\nRETRO\nREUMA\nREUNA\nREUNE\nREUNI\nREUNO\nREVEA\nREVED\nREVEN\nREVEO\nREVER\nREVES\nREVIO\nREYAD\nREYAN\nREYAR\nREYAS\nREYEN\nREYES\nREZAD\nREZAN\nREZAR\nREZAS\nREZNO\nREZON\nREZOS\nRENIA\nRENID\nRENIR\nRENIS\nRIADA\nRIAIS\nRIATA\nRIBAS\nRICAS\nRICEN\nRICES\nRICIA\nRICIO\nRICOS\nRIEGA\nRIEGO\nRIELA\nRIELE\nRIELO\nRIERA\nRIERE\nRIESE\nRIFAD\nRIFAN\nRIFAR\nRIFAS\nRIFEN\nRIFES\nRIFLE\nRIGEN\nRIGES\nRIGIL\nRIGIO\nRIGOR\nRIGUA\nRIGUE\nRIJAN\nRIJAS\nRIJOS\nRILAD\nRILAN\nRILAR\nRILAS\nRILEN\nRILES\nRIMAD\nRIMAN\nRIMAR\nRIMAS\nRIMEL\nRIMEN\nRIMES\nRIMUS\nRINDA\nRINDE\nRINDO\nRINGA\nRINGO\nRIOJA\nRIPIA\nRIPIE\nRIPIO\nRISAS\nRISCA\nRISCO\nRISOS\nRISPA\nRISPE\nRISPO\nRITMA\nRITME\nRITMO\nRITON\nRITOS\nRIVAL\nRIZAD\nRIZAL\nRIZAN\nRIZAR\nRIZAS\nRIZON\nRIZOS\nRINAN\nRINAS\nRINEN\nRINES\nRINON\nROAIS\nROANA\nROANO\nROBAD\nROBAN\nROBAR\nROBAS\nROBDA\nROBEN\nROBES\nROBIN\nROBLA\nROBLE\nROBLO\nROBOS\nROBOT\nROBRA\nROBRE\nROCAS\nROCEA\nROCEE\nROCEN\nROCEO\nROCES\nROCHA\nROCHE\nROCHO\nROCIA\nROCIE\nROCIN\nROCIO\nROCOS\nRODAD\nRODAL\nRODAO\nRODAR\nRODAS\nRODEA\nRODEE\nRODEO\nRODIA\nRODIL\nRODIO\nRODOS\nROEIS\nROELA\nROERA\nROERE\nROETE\nROGAD\nROGAR\nROGAS\nROGOS\nROGUE\nROIAN\nROIAS\nROIDA\nROIDO\nROIGA\nROIGO\nROJAL\nROJAS\nROJEA\nROJEE\nROJEO\nROJEZ\nROJOS\nROLAD\nROLAN\nROLAR\nROLAS\nROLDA\nROLDE\nROLDO\nROLEN\nROLEO\nROLES\nROLLA\nROLLE\nROLLO\nROLOS\nROMAN\nROMAS\nROMBO\nROMEA\nROMEO\nROMIN\nROMIS\nROMOS\nROMPA\nROMPE\nROMPI\nROMPO\nRONCA\nRONCE\nRONCO\nRONDA\nRONDE\nRONDO\nRONES\nRONZA\nRONZO\nROPAS\nROPON\nROQUE\nRORAD\nRORAN\nRORAR\nRORAS\nROREN\nRORES\nRORRO\nROSAL\nROSAN\nROSAR\nROSAS\nROSCA\nROSCO\nROSEA\nROSEE\nROSEN\nROSEO\nROSES\nROSJO\nROSON\nROSOS\nROSTA\nROSTE\nROSTI\nROSTO\nROTAD\nROTAL\nROTAN\nROTAR\nROTAS\nROTEN\nROTES\nROTOR\nROTOS\nROUGE\nROYAN\nROYAS\nROYOS\nROZAD\nROZAN\nROZAR\nROZAS\nROZNA\nROZNE\nROZNO\nROZON\nROZOS\nRONAD\nRONAL\nRONAN\nRONAR\nRONAS\nRONEN\nRONIA\nRUABA\nRUADA\nRUADO\nRUAIS\nRUANA\nRUANO\nRUARA\nRUARE\nRUASE\nRUBEA\nRUBEO\nRUBIA\nRUBIN\nRUBIO\nRUBIS\nRUBLO\nRUBOR\nRUBRA\nRUBRO\nRUCAD\nRUCAN\nRUCAR\nRUCAS\nRUCHA\nRUCHE\nRUCHO\nRUCIA\nRUCIO\nRUCOS\nRUDAS\nRUDOS\nRUECA\nRUEDA\nRUEDE\nRUEDO\nRUEGA\nRUEGO\nRUEIS\nRUEJO\nRUENO\nRUFAS\nRUFON\nRUFOS\nRUGAD\nRUGAN\nRUGAR\nRUGAS\nRUGBY\nRUGEN\nRUGES\nRUGIA\nRUGID\nRUGIO\nRUGIR\nRUGIS\nRUGUE\nRUIDO\nRUINA\nRUINE\nRUINO\nRUJAN\nRUJAS\nRUJIA\nRUJIE\nRUJIO\nRULAD\nRULAN\nRULAR\nRULAS\nRULEN\nRULES\nRULOS\nRUMBA\nRUMBE\nRUMBO\nRUMIA\nRUMIE\nRUMIO\nRUMIS\nRUMOR\nRUMOS\nRUNAS\nRUNES\nRUNGA\nRUNGO\nRUNOS\nRUPIA\nRUQUE\nRURAL\nRURRU\nRUSAS\nRUSCO\nRUSEL\nRUSES\nRUSIA\nRUSOS\nRUSTA\nRUSTE\nRUSTI\nRUSTO\nRUTAD\nRUTAN\nRUTAR\nRUTAS\nRUTEL\nRUTEN\nRUTES\nRUNAD\nRUNAN\nRUNAR\nRUNEN\nRUNIA\nRUNID\nRUNIR\nRUNIS\nSABEA\nSABED\nSABEN\nSABEO\nSABER\nSABES\nSABIA\nSABIO\nSABIR\nSABLE\nSABOR\nSABRA\nSABRE\nSACAD\nSACAN\nSACAR\nSACAS\nSACES\nSACHA\nSACHE\nSACHO\nSACIA\nSACIE\nSACIO\nSACON\nSACOS\nSACRA\nSACRE\nSACRO\nSAETA\nSAETE\nSAETI\nSAETO\nSAFIR\nSAGAS\nSAGAZ\nSAGUS\nSAINA\nSAINE\nSAINO\nSAJAD\nSAJAN\nSAJAR\nSAJAS\nSAJEN\nSAJES\nSAJIA\nSAJON\nSALAD\nSALAN\nSALAR\nSALAS\nSALAZ\nSALCE\nSALDA\nSALDE\nSALDO\nSALEA\nSALEE\nSALEN\nSALEO\nSALEP\nSALES\nSALGA\nSALGO\nSALIA\nSALID\nSALIN\nSALIO\nSALIR\nSALIS\nSALLA\nSALLE\nSALLO\nSALMA\nSALME\nSALMO\nSALOL\nSALON\nSALPA\nSALSA\nSALSO\nSALTA\nSALTE\nSALTO\nSALUD\nSALVA\nSALVE\nSALVO\nSAMAN\nSAMAS\nSAMBA\nSAMBO\nSAMIA\nSAMIO\nSAMPA\nSANAD\nSANAN\nSANAR\nSANAS\nSANCO\nSANEA\nSANEE\nSANEN\nSANEO\nSANES\nSANGO\nSANIE\nSANJA\nSANJE\nSANJO\nSANOS\nSANSA\nSANSO\nSANTA\nSANTO\nSAPAS\nSAPEA\nSAPEE\nSAPEO\nSAPOS\nSAQUE\nSARAN\nSARAO\nSARDA\nSARDE\nSARDO\nSARGA\nSARGO\nSARIA\nSARIS\nSARNA\nSARRO\nSARTA\nSARZA\nSARZO\nSASAL\nSATAN\nSATAS\nSATEN\nSATIN\nSATIS\nSATOS\nSAUCE\nSAUCO\nSAUDI\nSAUNA\nSAVIA\nSAXEA\nSAXEO\nSAXOS\nSAYAL\nSAYAS\nSAYON\nSAYOS\nSAZON\nSEAIS\nSEBES\nSEBOS\nSECAD\nSECAN\nSECAR\nSECAS\nSECON\nSECOS\nSECTA\nSECUA\nSEDAD\nSEDAL\nSEDAN\nSEDAR\nSEDAS\nSEDEA\nSEDEE\nSEDEN\nSEDEO\nSEDES\nSEGAD\nSEGAR\nSEGAS\nSEGRI\nSEGUE\nSEGUI\nSEGUN\nSEGUR\nSEIBO\nSEICO\nSEISE\nSEJES\nSELES\nSELLA\nSELLE\nSELLO\nSELVA\nSEMAS\nSEMEN\nSEMIS\nSENAS\nSENDA\nSENES\nSENIL\nSENOS\nSENTA\nSENTE\nSENTI\nSENTO\nSEORA\nSEPAN\nSEPAS\nSEPES\nSEPIA\nSEPTO\nSEQUE\nSERAN\nSERAS\nSERBA\nSERBO\nSERES\nSERIA\nSERIE\nSERIO\nSERNA\nSERON\nSERPA\nSERRA\nSERRE\nSERRO\nSERVI\nSERVO\nSESEA\nSESEE\nSESEN\nSESEO\nSESES\nSESGA\nSESGO\nSESIL\nSESIS\nSESMA\nSESMO\nSESOS\nSETAL\nSETAS\nSETOS\nSEXAD\nSEXAN\nSEXAR\nSEXAS\nSEXEN\nSEXES\nSEXMA\nSEXMO\nSEXOS\nSEXTA\nSEXTO\nSENAL\nSENOR\nSHORT\nSHUAR\nSIBIL\nSICLO\nSICUS\nSIDAS\nSIDRA\nSIEGA\nSIEGO\nSIENA\nSIESO\nSIETE\nSIFON\nSIFUE\nSIGAN\nSIGAS\nSIGLA\nSIGLO\nSIGMA\nSIGNA\nSIGNE\nSIGNO\nSIGUA\nSIGUE\nSIJES\nSIJUS\nSILBA\nSILBE\nSILBO\nSILES\nSILEX\nSILFO\nSILGA\nSILGO\nSILLA\nSILOS\nSILVA\nSIMAS\nSIMIA\nSIMIL\nSIMIO\nSIMON\nSIMPA\nSIMUN\nSINGA\nSINGO\nSINOS\nSIOUX\nSIPES\nSIQUE\nSIRAS\nSIRGA\nSIRGO\nSIRIA\nSIRIN\nSIRIO\nSIRLE\nSIROS\nSIRTE\nSIRVA\nSIRVE\nSIRVO\nSISAD\nSISAL\nSISAN\nSISAR\nSISAS\nSISCA\nSISEA\nSISEE\nSISEN\nSISEO\nSISES\nSISMO\nSISON\nSITAS\nSITIA\nSITIE\nSITIO\nSITOS\nSITUA\nSITUE\nSITUO\nSOASA\nSOASE\nSOASO\nSOBAD\nSOBAN\nSOBAR\nSOBAS\nSOBEN\nSOBEO\nSOBES\nSOBON\nSOBOS\nSOBRA\nSOBRE\nSOBRO\nSOCAS\nSOCAZ\nSOCHE\nSOCIA\nSOCIO\nSOCOL\nSODAS\nSODIO\nSOEZA\nSOFAS\nSOFIS\nSOGAS\nSOGUN\nSOJAS\nSOLAD\nSOLAR\nSOLAS\nSOLAZ\nSOLDA\nSOLDE\nSOLDO\nSOLEA\nSOLEE\nSOLEN\nSOLEO\nSOLER\nSOLES\nSOLFA\nSOLIA\nSOLIO\nSOLLA\nSOLLO\nSOLOS\nSOLTA\nSOLTE\nSOLTO\nSOMAS\nSOMOS\nSONAD\nSONAR\nSONAS\nSONDA\nSONDE\nSONDO\nSONES\nSONIO\nSONSA\nSONSO\nSONTA\nSONTO\nSOPAD\nSOPAN\nSOPAR\nSOPAS\nSOPEA\nSOPEE\nSOPEN\nSOPEO\nSOPES\nSOPIE\nSOPLA\nSOPLE\nSOPLO\nSOPON\nSOPOR\nSORBA\nSORBE\nSORBI\nSORBO\nSORCE\nSORDA\nSORDO\nSORES\nSORGO\nSORNA\nSORNE\nSORNO\nSOROR\nSOROS\nSORRA\nSOSAL\nSOSAR\nSOSAS\nSOSIA\nSOSOS\nSOTAD\nSOTAN\nSOTAR\nSOTAS\nSOTEN\nSOTES\nSOTIL\nSOTOL\nSOTOS\nSOVOZ\nSOYAS\nSPORT\nSPRAY\nSTAND\nSUABA\nSUABO\nSUATA\nSUATO\nSUAVE\nSUAZI\nSUBAN\nSUBAS\nSUBEN\nSUBEO\nSUBES\nSUBIA\nSUBID\nSUBIO\nSUBIR\nSUBIS\nSUBTE\nSUCHE\nSUCIA\nSUCIO\nSUCOS\nSUCRE\nSUCUS\nSUDAD\nSUDAN\nSUDAR\nSUDAS\nSUDEN\nSUDES\nSUDOR\nSUECA\nSUECO\nSUELA\nSUELE\nSUELO\nSUENA\nSUENE\nSUENO\nSUERO\nSUEVA\nSUEVO\nSUFIS\nSUFRA\nSUFRE\nSUFRI\nSUFRO\nSUIDO\nSUITA\nSUITE\nSUIZA\nSUIZO\nSULAS\nSULCO\nSULLA\nSUMAD\nSUMAN\nSUMAR\nSUMAS\nSUMEN\nSUMES\nSUMIA\nSUMID\nSUMIO\nSUMIR\nSUMIS\nSUMOS\nSUMUS\nSUNCA\nSUNCO\nSUPER\nSUPLA\nSUPLE\nSUPLI\nSUPLO\nSUPON\nSUPRA\nSURAL\nSURAS\nSURCA\nSURCO\nSURDA\nSURDE\nSURDI\nSURDO\nSURES\nSURFS\nSURGE\nSURGI\nSURIS\nSURJA\nSURJO\nSURTA\nSURTE\nSURTI\nSURTO\nSUSES\nSUSTO\nSUTAS\nSUTES\nSUTIL\nSUYAS\nSUYOS\nSUZON\nTABAL\nTABAS\nTABEA\nTABES\nTABIS\nTABLA\nTABLE\nTABLO\nTABON\nTABOR\nTABOS\nTABUS\nTACAR\nTACAS\nTACEN\nTACES\nTACET\nTACHA\nTACHE\nTACHO\nTACON\nTACOS\nTACTO\nTAFIA\nTAFON\nTAFOS\nTAFUR\nTAGUA\nTAHAS\nTAHUR\nTAIFA\nTAIGA\nTAIMA\nTAIME\nTAIMO\nTAINA\nTAINO\nTAIPA\nTAIRA\nTAIRE\nTAITA\nTAJAD\nTAJAN\nTAJAR\nTAJAS\nTAJEA\nTAJEE\nTAJEN\nTAJEO\nTAJES\nTAJIN\nTAJON\nTAJOS\nTAJUS\nTALAD\nTALAN\nTALAR\nTALAS\nTALCO\nTALEA\nTALED\nTALEN\nTALES\nTALGO\nTALIN\nTALIO\nTALLA\nTALLE\nTALLO\nTALMA\nTALON\nTALOS\nTALPA\nTALUD\nTAMAL\nTAMBA\nTAMBO\nTAMIL\nTAMIZ\nTAMOS\nTAMUL\nTANAS\nTANCA\nTANCO\nTANDA\nTANES\nTANGA\nTANGE\nTANGI\nTANGO\nTANJA\nTANJO\nTANOR\nTANOS\nTANTA\nTANTO\nTANZA\nTAPAD\nTAPAN\nTAPAR\nTAPAS\nTAPEA\nTAPEE\nTAPEN\nTAPEO\nTAPES\nTAPIA\nTAPIE\nTAPIN\nTAPIO\nTAPIR\nTAPIS\nTAPIZ\nTAPON\nTAQUE\nTARAD\nTARAN\nTARAR\nTARAS\nTARAY\nTARCA\nTARCO\nTARDA\nTARDE\nTARDO\nTAREA\nTAREN\nTARES\nTARIN\nTARJA\nTARJE\nTARJO\nTARMA\nTAROT\nTARRA\nTARRO\nTARSO\nTARTA\nTASAD\nTASAN\nTASAR\nTASAS\nTASCA\nTASCO\nTASEN\nTASES\nTASIA\nTASIO\nTASIS\nTASTO\nTATAS\nTATAY\nTATOS\nTATUA\nTATUE\nTATUO\nTATUS\nTAUCA\nTAUCO\nTAULA\nTAURO\nTAXIS\nTAXON\nTAYOS\nTAYUL\nTAZAD\nTAZAN\nTAZAR\nTAZAS\nTAZON\nTANAD\nTANAN\nTANAR\nTANED\nTANEN\nTANER\nTANIA\nTEAME\nTEBEA\nTEBEO\nTECAS\nTECES\nTECHA\nTECHE\nTECHO\nTECLA\nTECLE\nTECLO\nTECOL\nTEDAS\nTEDIO\nTEFES\nTEGEA\nTEGEO\nTEGUA\nTEGUE\nTEHUL\nTEINA\nTEJAD\nTEJAN\nTEJAR\nTEJAS\nTEJED\nTEJEN\nTEJER\nTEJES\nTEJIA\nTEJIO\nTEJON\nTEJOS\nTELAR\nTELAS\nTELES\nTELEX\nTELON\nTEMAD\nTEMAN\nTEMAR\nTEMAS\nTEMED\nTEMEN\nTEMER\nTEMES\nTEMIA\nTEMIO\nTEMOR\nTEMPO\nTEMUS\nTENAS\nTENAZ\nTENCA\nTENDE\nTENDI\nTENED\nTENER\nTENES\nTENGA\nTENGO\nTENIA\nTENIO\nTENIS\nTENOR\nTENSA\nTENSE\nTENSO\nTENTA\nTENTE\nTENTO\nTENUE\nTEOSA\nTEOSO\nTEPES\nTEPUS\nTEPUY\nTEQUE\nTERCA\nTERCO\nTERMA\nTERMO\nTERNA\nTERNE\nTERNO\nTEROS\nTERSA\nTERSE\nTERSO\nTESAD\nTESAN\nTESAR\nTESAS\nTESEN\nTESES\nTESIS\nTESLA\nTESON\nTESOS\nTESTA\nTESTE\nTESTO\nTETAD\nTETAN\nTETAR\nTETAS\nTETEN\nTETES\nTETON\nTETRA\nTETRO\nTEXES\nTEXTO\nTEYAS\nTEYOS\nTEYUS\nTENID\nTENIR\nTIACA\nTIARA\nTIBAR\nTIBES\nTIBIA\nTIBIE\nTIBIO\nTIBOR\nTICAS\nTICOS\nTIENE\nTIESA\nTIESO\nTIFAS\nTIFON\nTIFOS\nTIFUS\nTIGRA\nTIGRE\nTIGUA\nTIGUE\nTIJAS\nTIJOS\nTIJUL\nTILAS\nTILDA\nTILDE\nTILDO\nTILES\nTILIA\nTILIN\nTILLA\nTILLE\nTILLO\nTILMA\nTILOS\nTIMAD\nTIMAN\nTIMAR\nTIMAS\nTIMBA\nTIMBO\nTIMEN\nTIMES\nTIMOL\nTIMON\nTIMOS\nTIMPA\nTINAS\nTINCA\nTINCO\nTINEA\nTINEO\nTINGE\nTINOS\nTINTA\nTINTE\nTINTO\nTIPAS\nTIPIS\nTIPLE\nTIPOI\nTIPOS\nTIPOY\nTIQUE\nTIQUI\nTIRAD\nTIRAN\nTIRAR\nTIRAS\nTIREN\nTIRES\nTIRIA\nTIRIO\nTIRON\nTIROS\nTIRRO\nTIRSO\nTIRTE\nTISIS\nTISTE\nTISUS\nTITAD\nTITAN\nTITAR\nTITAS\nTITEA\nTITEE\nTITEN\nTITEO\nTITES\nTITIL\nTITIS\nTITOS\nTIZAS\nTIZNA\nTIZNE\nTIZNO\nTIZON\nTIZOS\nTINAN\nTINEN\nTINES\nTLACO\nTOABA\nTOADA\nTOADO\nTOAIS\nTOARA\nTOARE\nTOASE\nTOBAR\nTOBAS\nTOCAD\nTOCAN\nTOCAR\nTOCAS\nTOCEN\nTOCES\nTOCHA\nTOCHE\nTOCHO\nTOCIA\nTOCIO\nTOCON\nTOCOS\nTOCTE\nTODAS\nTODIA\nTODOS\nTOEIS\nTOESA\nTOFOS\nTOGAN\nTOGAR\nTOGAS\nTOGUE\nTOJAL\nTOJOS\nTOLAS\nTOLDA\nTOLDE\nTOLDO\nTOLES\nTOLLA\nTOLLO\nTOLMO\nTOLON\nTOLVA\nTOMAD\nTOMAN\nTOMAR\nTOMAS\nTOMEN\nTOMES\nTOMIN\nTOMON\nTOMOS\nTONAD\nTONAL\nTONAN\nTONAR\nTONAS\nTONCA\nTONDO\nTONEL\nTONEN\nTONER\nTONES\nTONGA\nTONGO\nTONOS\nTONTA\nTONTO\nTOPAD\nTOPAN\nTOPAR\nTOPAS\nTOPEA\nTOPEE\nTOPEN\nTOPEO\nTOPES\nTOPIA\nTOPIL\nTOPON\nTOPOS\nTOQUE\nTOQUI\nTORAL\nTORAS\nTORAX\nTORCA\nTORCE\nTORCI\nTORCO\nTORDA\nTORDO\nTOREA\nTOREE\nTOREO\nTORES\nTORGA\nTORGO\nTORIL\nTORIO\nTORMO\nTORNA\nTORNE\nTORNO\nTORON\nTOROS\nTORPE\nTORRA\nTORRE\nTORRO\nTORSO\nTORTA\nTORVA\nTORVO\nTOSAN\nTOSAS\nTOSCA\nTOSCO\nTOSED\nTOSEN\nTOSER\nTOSES\nTOSIA\nTOSIO\nTOSTA\nTOSTE\nTOSTO\nTOTAL\nTOTEM\nTOTES\nTOTIS\nTOTOL\nTOVAS\nTOZAD\nTOZAL\nTOZAN\nTOZAR\nTOZAS\nTOZOS\nTONIL\nTRABA\nTRABE\nTRABO\nTRACA\nTRACE\nTRAED\nTRAEN\nTRAER\nTRAES\nTRAFA\nTRAGA\nTRAGO\nTRAIA\nTRAJE\nTRAJO\nTRAMA\nTRAME\nTRAMO\nTRAPA\nTRAPE\nTRAPO\nTRARO\nTRATA\nTRATE\nTRATO\nTRAVO\nTRAZA\nTRAZO\nTREBO\nTRECE\nTREFE\nTREJA\nTREMA\nTREME\nTREMI\nTREMO\nTRENA\nTRENO\nTREOS\nTREPA\nTREPE\nTREPO\nTRETA\nTRIAD\nTRIAL\nTRIAN\nTRIAR\nTRIAS\nTRIBU\nTRICE\nTRIEN\nTRIES\nTRIGA\nTRIGO\nTRILE\nTRINA\nTRINE\nTRINO\nTRIOS\nTRIPA\nTRIPE\nTRISA\nTRISE\nTRISO\nTRIZA\nTRIZO\nTROCA\nTROCE\nTROCO\nTROJA\nTROJE\nTROLA\nTROLE\nTRONA\nTRONE\nTRONO\nTROPA\nTROPO\nTROTA\nTROTE\nTROTO\nTROVA\nTROVE\nTROVO\nTROZA\nTROZO\nTRUCA\nTRUCO\nTRUES\nTRUFA\nTRUFE\nTRUFO\nTRUJA\nTRUSA\nTRUST\nTUANI\nTUBAS\nTUBOS\nTUCAN\nTUCAS\nTUCIA\nTUCOS\nTUCUN\nTUDAS\nTUDEL\nTUECA\nTUECO\nTUERA\nTUERO\nTUFEA\nTUFEE\nTUFEO\nTUFOS\nTUINA\nTULAR\nTULES\nTULIO\nTULLA\nTULLE\nTULLI\nTULLO\nTULPA\nTUMBA\nTUMBE\nTUMBO\nTUMOR\nTUMOS\nTUNAD\nTUNAL\nTUNAN\nTUNAR\nTUNAS\nTUNCA\nTUNCO\nTUNDA\nTUNDE\nTUNDI\nTUNDO\nTUNEA\nTUNEE\nTUNEL\nTUNEN\nTUNEO\nTUNES\nTUNJO\nTUNOS\nTUNTA\nTUPAN\nTUPAS\nTUPEN\nTUPES\nTUPIA\nTUPID\nTUPIN\nTUPIO\nTUPIR\nTUPIS\nTUPOS\nTURAR\nTURBA\nTURBE\nTURBO\nTURCA\nTURCO\nTURMA\nTURNA\nTURNE\nTURNO\nTURON\nTURRA\nTURRE\nTURRO\nTUSAD\nTUSAN\nTUSAR\nTUSAS\nTUSCA\nTUSCO\nTUSEN\nTUSES\nTUSON\nTUSOS\nTUTAS\nTUTEA\nTUTEE\nTUTEO\nTUTES\nTUTIA\nTUTOR\nTUTOS\nTUTUS\nTUYAS\nTUYOS\nTUZAS\nUBICA\nUBICO\nUBIES\nUBIOS\nUBRES\nUCASE\nUCHUS\nUEBOS\nUFANA\nUFANE\nUFANO\nUGRES\nUJIER\nUJULE\nULAGA\nULALA\nULANO\nULEMA\nULPOS\nULTRA\nULUAS\nULULA\nULULE\nULULO\nUMBRA\nUMBRO\nUMERO\nUNAIS\nUNCEN\nUNCES\nUNCIA\nUNCID\nUNCIO\nUNCIR\nUNCIS\nUNGEN\nUNGES\nUNGIA\nUNGID\nUNGIO\nUNGIR\nUNGIS\nUNIAN\nUNIAS\nUNICA\nUNICO\nUNIDA\nUNIDO\nUNION\nUNIRA\nUNIRE\nUNJAN\nUNJAS\nUNTAD\nUNTAN\nUNTAR\nUNTAS\nUNTEN\nUNTES\nUNTOS\nUNZAN\nUNZAS\nUPABA\nUPADA\nUPADO\nUPAIS\nUPARA\nUPARE\nUPASE\nUPEIS\nUPUPA\nURAOS\nURAPE\nURATO\nURBES\nURCAS\nURCES\nURDAN\nURDAS\nURDEN\nURDES\nURDIA\nURDID\nURDIO\nURDIR\nURDIS\nURDUS\nUREAS\nURGEN\nURGES\nURGIA\nURGID\nURGIO\nURGIR\nURGIS\nURICA\nURICO\nURJAN\nURJAS\nURNAS\nURTAS\nURUBU\nURUCU\nURUGA\nUSABA\nUSADA\nUSADO\nUSAIS\nUSAJE\nUSARA\nUSARE\nUSASE\nUSEIS\nUSGOS\nUSIAS\nUSIER\nUSINA\nUSTED\nUSUAL\nUSURA\nUSURE\nUSURO\nUTERO\nUVADA\nUVATE\nUVEAS\nUVERA\nUVERO\nUVIAR\nUVULA\nUNADA\nUNADO\nUNATE\nUNERA\nUNERE\nUNERO\nUNESE\nUNETA\nUNOSA\nUNOSO\nUNUDO\nVACAD\nVACAN\nVACAR\nVACAS\nVACIA\nVACIE\nVACIO\nVACOS\nVACUA\nVACUO\nVADEA\nVADEE\nVADEO\nVADES\nVADOS\nVAFEA\nVAFEE\nVAFEO\nVAGAD\nVAGAN\nVAGAR\nVAGAS\nVAGON\nVAGOS\nVAGUE\nVAHAD\nVAHAN\nVAHAR\nVAHAS\nVAHEA\nVAHEE\nVAHEN\nVAHEO\nVAHES\nVAHOS\nVAIDA\nVAINA\nVAJEA\nVAJEE\nVAJEO\nVALAR\nVALED\nVALEN\nVALER\nVALES\nVALET\nVALGA\nVALGO\nVALIA\nVALIO\nVALIS\nVALLA\nVALLE\nVALLO\nVALON\nVALOR\nVALSA\nVALSE\nVALSO\nVALUA\nVALUE\nVALUO\nVALVA\nVAMOS\nVANAS\nVANEA\nVANEE\nVANEO\nVANOS\nVAPOR\nVAQUE\nVARAD\nVARAL\nVARAN\nVARAR\nVARAS\nVAREA\nVAREE\nVAREN\nVAREO\nVARES\nVARGA\nVARIA\nVARIE\nVARIO\nVARIS\nVARIZ\nVARON\nVASAR\nVASAS\nVASCA\nVASCO\nVASOS\nVASTA\nVASTO\nVATER\nVATES\nVATIO\nVAYAN\nVAYAS\nVEAIS\nVECEN\nVECES\nVEDAD\nVEDAN\nVEDAR\nVEDAS\nVEDEN\nVEDES\nVEGAS\nVEIAN\nVEIAS\nVEJAD\nVEJAN\nVEJAR\nVEJAS\nVEJEN\nVEJES\nVEJEZ\nVELAD\nVELAN\nVELAR\nVELAS\nVELAY\nVELEN\nVELES\nVELIS\nVELIZ\nVELLO\nVELON\nVELOS\nVELOZ\nVEMOS\nVENAL\nVENAS\nVENCE\nVENCI\nVENDA\nVENDE\nVENDI\nVENDO\nVENGA\nVENGO\nVENIA\nVENID\nVENIR\nVENIS\nVENTA\nVENTE\nVENTO\nVENUS\nVENZA\nVENZO\nVERAN\nVERAS\nVERAZ\nVERBA\nVERBO\nVERDE\nVERES\nVERGA\nVERGE\nVERIA\nVERIL\nVERJA\nVERME\nVERMU\nVEROS\nVERSA\nVERSE\nVERSO\nVERTE\nVERTI\nVESTE\nVESTI\nVETAD\nVETAN\nVETAR\nVETAS\nVETEA\nVETEE\nVETEN\nVETEO\nVETES\nVETON\nVETOS\nVEZAD\nVEZAN\nVEZAR\nVEZAS\nVIADA\nVIAJA\nVIAJE\nVIAJO\nVIBRA\nVIBRE\nVIBRO\nVICHA\nVICHE\nVICHO\nVICHY\nVICIA\nVICIE\nVICIO\nVICOS\nVICTO\nVIDAS\nVIDEO\nVIDES\nVIDON\nVIDRO\nVIEJA\nVIEJO\nVIENE\nVIERA\nVIERE\nVIESA\nVIESE\nVIGAS\nVIGIA\nVIGIE\nVIGIO\nVIGOR\nVILES\nVILLA\nVILOS\nVIMOS\nVINAL\nVINAR\nVINCA\nVINCO\nVINOS\nVINTA\nVIOLA\nVIOLE\nVIOLO\nVIRAD\nVIRAL\nVIRAN\nVIRAR\nVIRAS\nVIREN\nVIREO\nVIRES\nVIRGO\nVIRIL\nVIRIO\nVIROL\nVIRON\nVIRUS\nVISAD\nVISAN\nVISAR\nVISAS\nVISCO\nVISEA\nVISEE\nVISEN\nVISEO\nVISES\nVISIR\nVISON\nVISOR\nVISOS\nVISTA\nVISTE\nVISTO\nVITAD\nVITAL\nVITAN\nVITAR\nVITAS\nVITEN\nVITES\nVITOR\nVITOS\nVITRE\nVIUDA\nVIUDO\nVIVAC\nVIVAD\nVIVAN\nVIVAR\nVIVAS\nVIVAZ\nVIVEN\nVIVES\nVIVEZ\nVIVIA\nVIVID\nVIVIO\nVIVIR\nVIVIS\nVIVON\nVIVOS\nVINAS\nVOACE\nVOCAL\nVOCEA\nVOCEE\nVOCEO\nVOCES\nVODCA\nVODUS\nVOILA\nVOLAD\nVOLAR\nVOLAS\nVOLCA\nVOLCO\nVOLEA\nVOLEE\nVOLEO\nVOLON\nVOLTS\nVOLVE\nVOLVI\nVOLVO\nVOMER\nVORAZ\nVOSEA\nVOSEE\nVOSEO\nVOTAD\nVOTAN\nVOTAR\nVOTAS\nVOTEN\nVOTES\nVOTOS\nVOTRI\nVOZNA\nVOZNE\nVOZNO\nVUDUS\nVUELA\nVUELE\nVUELO\nVUESA\nVUESO\nVULGO\nVULTO\nVULVA\nVUSCO\nXECAS\nXENON\nXINCA\nXIOTE\nXOLAS\nXOLOS\nYABAS\nYACAL\nYACAS\nYACED\nYACEN\nYACER\nYACES\nYACIA\nYACIO\nYACON\nYAGAN\nYAGAS\nYAGUA\nYAITI\nYALES\nYAMAO\nYAMBO\nYAMPA\nYANAS\nYANTA\nYANTE\nYANTO\nYAPAD\nYAPAN\nYAPAR\nYAPAS\nYAPEN\nYAPES\nYAPUS\nYAQUE\nYAQUI\nYARDA\nYARES\nYAREY\nYAROS\nYATAI\nYATAY\nYATES\nYAYAS\nYAYOS\nYAZCA\nYAZCO\nYAZGA\nYAZGO\nYEBOS\nYECOS\nYEDGO\nYEDRA\nYEGUA\nYELGO\nYELMO\nYEMAS\nYENDO\nYENES\nYENTE\nYERAL\nYERBA\nYERGA\nYERGO\nYERMA\nYERME\nYERMO\nYERNA\nYERNO\nYEROS\nYERRA\nYERRE\nYERRO\nYERSI\nYERTA\nYERTO\nYERVO\nYESAL\nYESAR\nYESCA\nYESON\nYESOS\nYETIS\nYEYES\nYEYOS\nYEZGO\nYINAS\nYINES\nYIRAS\nYIROS\nYODAD\nYODAN\nYODAR\nYODAS\nYODEN\nYODES\nYODOS\nYOGAR\nYOGAS\nYOGOS\nYOGUI\nYOGUR\nYOLAS\nYOQUI\nYORIS\nYOSES\nYOYOS\nYUCAL\nYUCAS\nYUCPA\nYUDOS\nYUGOS\nYUMBA\nYUMBO\nYUNGA\nYUNTA\nYUNTO\nYURAS\nYURES\nYUTAS\nYUTES\nYUYAL\nYUYOS\nZABRA\nZABRO\nZACAS\nZACEA\nZACEE\nZACEO\nZADES\nZAFAD\nZAFAN\nZAFAR\nZAFAS\nZAFEN\nZAFES\nZAFIA\nZAFIO\nZAFIR\nZAFON\nZAFOS\nZAFRA\nZAFRE\nZAGAL\nZAGAS\nZAGUA\nZAHEN\nZAHON\nZAIDA\nZAINA\nZAINO\nZAJON\nZALAS\nZALBA\nZALBO\nZALEA\nZALEE\nZALEO\nZALLA\nZALLE\nZALLO\nZAMBA\nZAMBO\nZAMPA\nZAMPE\nZAMPO\nZANAS\nZANCA\nZANCO\nZANGA\nZANJA\nZANJE\nZANJO\nZAPAD\nZAPAN\nZAPAR\nZAPAS\nZAPEA\nZAPEE\nZAPEN\nZAPEO\nZAPES\nZAQUE\nZARBO\nZARCA\nZARCO\nZARES\nZARJA\nZARPA\nZARPE\nZARPO\nZARZA\nZARZO\nZATAS\nZAYAS\nZAZAS\nZAZOS\nZEBRA\nZEDAS\nZEGRI\nZEINA\nZEJEL\nZENDA\nZENDO\nZENES\nZENIT\nZETAS\nZINCS\nZOCAD\nZOCAN\nZOCAR\nZOCAS\nZOCLO\nZOCOS\nZOFRA\nZOILO\nZOIZO\nZOLLE\nZOMAS\nZOMBI\nZOMOS\nZOMPA\nZOMPO\nZONAL\nZONAS\nZONDA\nZONTA\nZONTO\nZONZA\nZONZO\nZOPAS\nZOPES\nZOPOS\nZOQUE\nZORRA\nZORRO\nZOTAL\nZOTES\nZOTOL\nZUAVO\nZUBIA\nZUDAS\nZUECA\nZUECO\nZUELA\nZUIZA\nZULLA\nZULLE\nZULLO\nZULUS\nZUMAS\nZUMBA\nZUMBE\nZUMBO\nZUMOS\nZUNAS\nZUNZA\nZUPIA\nZURAS\nZURBA\nZURCE\nZURCI\nZURDA\nZURDE\nZURDI\nZURDO\nZUREA\nZUREE\nZUREO\nZUROS\nZURRA\nZURRE\nZURRI\nZURRO\nZURZA\nZURZO\nZUZAR\nZUZON\nZUNAN\nZUNEN\nZUNES\nZUNIA\nZUNID\nZUNIR\nZUNIS\nZUNOS\nNAJOS\nNAMES\nNAMPI\nNANDU\nNANGA\nNANGO\nNATEA\nNATEE\nNATEO\nNANOS\nNECAS\nNECLA\nNECOS\nNENGA\nNENGO\nNEQUE\nNINGA\nNIPES\nNIQUE\nNIRES\nNISCA\nNIZCA\nNOCHA\nNOCLO\nNOCOS\nNOLAS\nNONGA\nNONGO\nNOQUI\nNORAS\nNORBO\nNORES\nNONEZ\nNURDA\nNURDO\nNUTAS\nNUTOS\nNUZCO"
  },
  {
    "path": "core/src/main/res/raw/wordle_list_fr.txt",
    "content": "ABATS\nABBES\nABCES\nABETI\nABIMA\nABIME\nABOIE\nABOIS\nABOLI\nABORD\nABOTS\nABOUT\nABOYA\nABOYE\nABRIS\nABUSA\nABUSE\nACCES\nACCOT\nACCRU\nACCUS\nACERA\nACERE\nACHAT\nACIDE\nACIER\nACINI\nACMES\nACNES\nACONS\nACORE\nACRES\nACTAI\nACTAS\nACTAT\nACTEE\nACTER\nACTES\nACTEZ\nACTIF\nADAGE\nADENT\nADIEU\nADMET\nADMIS\nADMIT\nADNEE\nADNES\nADORA\nADORE\nADRET\nADULA\nADULE\nAEDES\nAEQUO\nAERAI\nAERAS\nAERAT\nAEREE\nAERER\nAERES\nAEREZ\nAFFIN\nAFFUT\nAGACA\nAGACE\nAGAMI\nAGAPE\nAGATE\nAGAVE\nAGEES\nAGENT\nAGHAS\nAGILE\nAGIOS\nAGIRA\nAGITA\nAGITE\nAGNAT\nAGORA\nAGREA\nAGREE\nAGRES\nAGUIS\nAHANA\nAHANE\nAHANS\nAHURI\nAICHE\nAIDAI\nAIDAS\nAIDAT\nAIDEE\nAIDER\nAIDES\nAIDEZ\nAIENT\nAIEUL\nAIEUX\nAIGLE\nAIGRE\nAIGRI\nAIGUE\nAIGUS\nAILEE\nAILES\nAILLA\nAILLE\nAIMAI\nAIMAS\nAIMAT\nAIMEE\nAIMER\nAIMES\nAIMEZ\nAINEE\nAINES\nAINSI\nAIOLI\nAIRAI\nAIRAS\nAIRAT\nAIRER\nAIRES\nAIREZ\nAISEE\nAISES\nAISYS\nAJONC\nAJOUR\nAJOUT\nALBUM\nALDIN\nALDOL\nALEAS\nALENE\nALEPH\nALESA\nALESE\nALFAS\nALGIE\nALGOL\nALGUE\nALIAS\nALIBI\nALIOS\nALISE\nALITA\nALITE\nALIZE\nALLAI\nALLAS\nALLAT\nALLEE\nALLER\nALLES\nALLEZ\nALLIA\nALLIE\nALMEE\nALOES\nALORS\nALOSE\nALPAX\nALPES\nALPHA\nALPIN\nALTOS\nALUNA\nALUNE\nALUNI\nALUNS\nALVIN\nALYTE\nAMANT\nAMATI\nAMBLA\nAMBLE\nAMBON\nAMBRA\nAMBRE\nAMENA\nAMENE\nAMERE\nAMERS\nAMIBE\nAMICT\nAMIDE\nAMIES\nAMINE\nAMONT\nAMOUR\nAMPHI\nAMPLE\nAMPLI\nAMUIE\nAMUIS\nAMURA\nAMURE\nAMUSA\nAMUSE\nAMYLE\nANALE\nANAUX\nANCHE\nANCRA\nANCRE\nANDIN\nANETH\nANGES\nANGLE\nANGON\nANGOR\nANIER\nANIMA\nANIME\nANION\nANISA\nANISE\nANNAL\nANNEE\nANODE\nANONS\nANSEE\nANSES\nANTAN\nANTES\nANTRE\nAORTE\nAOUTA\nAOUTE\nAPHTE\nAPIOL\nAPION\nAPLAT\nAPNEE\nAPODE\nAPPAS\nAPPAT\nAPPEL\nAPPUI\nAPRES\nAPTES\nAPURA\nAPURE\nARABE\nARASA\nARASE\nARBRE\nARCHE\nARCON\nARDUE\nARDUS\nARECS\nARENE\nARETE\nARGAS\nARGON\nARGOT\nARGUA\nARGUE\nARGUS\nARIAS\nARIDE\nARIEN\nARISA\nARISE\nARMAI\nARMAS\nARMAT\nARMEE\nARMER\nARMES\nARMET\nARMEZ\nARMON\nAROME\nARQUA\nARQUE\nARRET\nARSIN\nARTEL\nARUMS\nARYEN\nARYLE\nASILE\nASPES\nASPIC\nASQUE\nASSAI\nASSES\nASSEZ\nASSIS\nASSIT\nASTER\nASTIS\nASTRE\nATELE\nATHEE\nATLAS\nATOLL\nATOME\nATONE\nATOUR\nATOUT\nAUBES\nAUBIN\nAUCUN\nAUDIO\nAUDIT\nAUGES\nAUGET\nAULNE\nAUNES\nAURAI\nAURAS\nAUREZ\nAUSSI\nAUTEL\nAUTOS\nAUTRE\nAVAIS\nAVAIT\nAVALA\nAVALE\nAVALS\nAVANT\nAVARE\nAVENS\nAVENU\nAVERA\nAVERE\nAVERS\nAVEUX\nAVIDE\nAVIEZ\nAVILI\nAVINA\nAVINE\nAVION\nAVISA\nAVISE\nAVISO\nAVIVA\nAVIVE\nAVOIR\nAVONS\nAVOUA\nAVOUE\nAVRIL\nAXAIS\nAXAIT\nAXANT\nAXEES\nAXENT\nAXERA\nAXIEZ\nAXILE\nAXONE\nAXONS\nAYANT\nAYONS\nAZOTE\nAZURA\nAZURE\nAZURS\nAZYME\nBABAS\nBABIL\nBABYS\nBACHA\nBACHE\nBACLA\nBACLE\nBACON\nBACUL\nBADGE\nBADIN\nBAFFA\nBAFFE\nBAFRA\nBAFRE\nBAGAD\nBAGNE\nBAGOU\nBAGUA\nBAGUE\nBAHUT\nBAIES\nBAINS\nBAISA\nBAISE\nBALAI\nBALES\nBALLA\nBALLE\nBALSA\nBALTE\nBANAL\nBANCO\nBANCS\nBANDA\nBANDE\nBANGS\nBANJO\nBANNA\nBANNE\nBANNI\nBARBA\nBARBE\nBARBU\nBARDA\nBARDE\nBARDS\nBARGE\nBARIL\nBARNS\nBARON\nBARRA\nBARRE\nBARRI\nBARYE\nBASAI\nBASAL\nBASAS\nBASAT\nBASEE\nBASER\nBASES\nBASEZ\nBASIC\nBASIN\nBASSE\nBASTE\nBATAI\nBATAS\nBATAT\nBATEE\nBATER\nBATES\nBATEZ\nBATIE\nBATIK\nBATIR\nBATIS\nBATIT\nBATON\nBATTE\nBATTU\nBAUDS\nBAUGE\nBAUME\nBAVAI\nBAVAS\nBAVAT\nBAVER\nBAVES\nBAVEZ\nBAYAI\nBAYAS\nBAYAT\nBAYER\nBAYES\nBAYEZ\nBAYOU\nBAZAR\nBEAIS\nBEAIT\nBEANT\nBEATE\nBEATS\nBEAUF\nBEAUX\nBEBES\nBECHA\nBECHE\nBECOT\nBECTA\nBECTE\nBEDON\nBEENT\nBEERA\nBEGUE\nBEGUM\nBEIEZ\nBEIGE\nBEKES\nBELAI\nBELAS\nBELAT\nBELEE\nBELER\nBELES\nBELEZ\nBELGE\nBELLE\nBELON\nBEMOL\nBENEF\nBENET\nBENIE\nBENIN\nBENIR\nBENIS\nBENIT\nBENNE\nBEONS\nBEQUA\nBEQUE\nBERCA\nBERCE\nBERET\nBERGE\nBERME\nBERNA\nBERNE\nBERYL\nBESEF\nBETAS\nBETEL\nBETES\nBETON\nBETTE\nBEURS\nBEVUE\nBIAIS\nBIBIS\nBIBLE\nBICHA\nBICHE\nBICOT\nBIDES\nBIDET\nBIDON\nBIEFS\nBIENS\nBIERE\nBIFFA\nBIFFE\nBIGLA\nBIGLE\nBIGOT\nBIGRE\nBIGUE\nBIJOU\nBILAI\nBILAN\nBILAS\nBILAT\nBILEE\nBILER\nBILES\nBILEZ\nBILLE\nBILLS\nBINAI\nBINAS\nBINAT\nBINEE\nBINER\nBINES\nBINEZ\nBINGO\nBIQUE\nBIRBE\nBISAI\nBISAS\nBISAT\nBISEE\nBISER\nBISES\nBISET\nBISEZ\nBISON\nBISOU\nBISSA\nBISSE\nBITAI\nBITAS\nBITAT\nBITEE\nBITER\nBITES\nBITEZ\nBITOS\nBITTA\nBITTE\nBIZUT\nBLACK\nBLAIR\nBLAMA\nBLAME\nBLANC\nBLAPS\nBLASA\nBLASE\nBLEDS\nBLEME\nBLEMI\nBLESA\nBLESE\nBLETS\nBLEUE\nBLEUI\nBLEUS\nBLOCS\nBLOND\nBLUES\nBLUET\nBLUFF\nBLUTA\nBLUTE\nBOBOS\nBOCAL\nBOCHE\nBOCKS\nBOETE\nBOEUF\nBOGIE\nBOGUE\nBOIRA\nBOIRE\nBOISA\nBOISE\nBOITA\nBOITE\nBOIVE\nBOLDO\nBOLEE\nBOLET\nBOMBA\nBOMBE\nBOMES\nBONDA\nBONDE\nBONDI\nBONDS\nBONIS\nBONNE\nBONTE\nBONUS\nBONZE\nBOOMS\nBOOTS\nBORAS\nBORAX\nBORDA\nBORDE\nBORDS\nBORES\nBORNA\nBORNE\nBORTS\nBOSCO\nBOSSA\nBOSSE\nBOSSU\nBOTES\nBOTTA\nBOTTE\nBOUCS\nBOUDA\nBOUDE\nBOUEE\nBOUES\nBOUGE\nBOUIF\nBOULA\nBOULE\nBOUME\nBOURG\nBOUSE\nBOUTA\nBOUTE\nBOUTS\nBOVIN\nBOXAI\nBOXAS\nBOXAT\nBOXEE\nBOXER\nBOXES\nBOXEZ\nBOYAU\nBRADA\nBRADE\nBRAIE\nBRAIS\nBRAIT\nBRAMA\nBRAME\nBRANS\nBRASA\nBRASE\nBRAVA\nBRAVE\nBRAVO\nBRAYA\nBRAYE\nBREAK\nBREFS\nBRELA\nBRELE\nBREME\nBREVE\nBRICK\nBRIDA\nBRIDE\nBRIES\nBRIFA\nBRIFE\nBRIMA\nBRIME\nBRINS\nBRIOS\nBRISA\nBRISE\nBROCS\nBRODA\nBRODE\nBROIE\nBROME\nBROOK\nBROUM\nBROUS\nBROUT\nBROYA\nBROYE\nBRUIE\nBRUIR\nBRUIS\nBRUIT\nBRULA\nBRULE\nBRUMA\nBRUME\nBRUNE\nBRUNI\nBRUNS\nBRUTE\nBRUTS\nBUBON\nBUCHA\nBUCHE\nBUEES\nBUGGY\nBUGLE\nBUIRE\nBULBE\nBULLE\nBUMES\nBURES\nBURIN\nBURON\nBUSCS\nBUSES\nBUSSE\nBUSTE\nBUTAI\nBUTAS\nBUTAT\nBUTEE\nBUTER\nBUTES\nBUTEZ\nBUTIN\nBUTOR\nBUTTA\nBUTTE\nBUVEE\nBUVEZ\nCABAN\nCABAS\nCABLA\nCABLE\nCABOT\nCABRA\nCABRE\nCABRI\nCABUS\nCACAO\nCACAS\nCACHA\nCACHE\nCADDY\nCADES\nCADET\nCADIS\nCADRA\nCADRE\nCADUC\nCAFES\nCAFRE\nCAFTA\nCAFTE\nCAGES\nCAGET\nCAGNA\nCAGNE\nCAGOT\nCAHOT\nCAIDS\nCAIEU\nCAIRN\nCAJOU\nCAJUN\nCAKES\nCALAI\nCALAO\nCALAS\nCALAT\nCALEE\nCALER\nCALES\nCALEZ\nCALFS\nCALIN\nCALMA\nCALME\nCALMI\nCALOS\nCALOT\nCALTA\nCALTE\nCALVA\nCAMEE\nCAMES\nCAMPA\nCAMPE\nCAMPS\nCAMUS\nCANAI\nCANAL\nCANAS\nCANAT\nCANDI\nCANER\nCANES\nCANEZ\nCANIF\nCANIN\nCANNA\nCANNE\nCANOE\nCANON\nCANOT\nCANUT\nCAOUA\nCAPEA\nCAPEE\nCAPES\nCAPON\nCAPOT\nCAPPA\nCAPRE\nCAPTA\nCAPTE\nCAQUA\nCAQUE\nCARAT\nCARDA\nCARDE\nCAREX\nCARGO\nCARIA\nCARIE\nCARIS\nCARMA\nCARME\nCARNE\nCARPE\nCARRA\nCARRE\nCARRY\nCARTE\nCASAI\nCASAS\nCASAT\nCASEE\nCASER\nCASES\nCASEZ\nCASSA\nCASSE\nCASTE\nCATCH\nCATIN\nCATIS\nCAURI\nCAUSA\nCAUSE\nCAVAI\nCAVAS\nCAVAT\nCAVEE\nCAVER\nCAVES\nCAVET\nCAVEZ\nCEANS\nCEDAI\nCEDAS\nCEDAT\nCEDEE\nCEDER\nCEDES\nCEDEX\nCEDEZ\nCEDRE\nCEINS\nCEINT\nCELAI\nCELAS\nCELAT\nCELEE\nCELER\nCELES\nCELEZ\nCELLA\nCELLE\nCELTE\nCELUI\nCENES\nCENSE\nCENTS\nCEPES\nCERAT\nCERCE\nCERFS\nCERNA\nCERNE\nCESAR\nCESSA\nCESSE\nCESTE\nCETTE\nCHAHS\nCHAIR\nCHAIS\nCHALE\nCHAMP\nCHANT\nCHAOS\nCHAPE\nCHARS\nCHATS\nCHAUD\nCHAUT\nCHAUX\nCHEFS\nCHEIK\nCHENE\nCHENU\nCHERA\nCHERE\nCHERI\nCHERS\nCHIAI\nCHIAS\nCHIAT\nCHICS\nCHIEE\nCHIEN\nCHIER\nCHIES\nCHIEZ\nCHINA\nCHINE\nCHIOT\nCHIPA\nCHIPE\nCHIPS\nCHOCS\nCHOIE\nCHOIR\nCHOIS\nCHOIT\nCHOIX\nCHOMA\nCHOME\nCHOPA\nCHOPE\nCHOSE\nCHOTT\nCHOUX\nCHOYA\nCHOYE\nCHUES\nCHUTA\nCHUTE\nCHYLE\nCHYME\nCIBLE\nCIDRE\nCIELS\nCIEUX\nCIGUE\nCILIE\nCILLA\nCILLE\nCIMES\nCINES\nCIPPE\nCIRAI\nCIRAS\nCIRAT\nCIREE\nCIRER\nCIRES\nCIREZ\nCIRON\nCIRRE\nCISTE\nCITAI\nCITAS\nCITAT\nCITEE\nCITER\nCITES\nCITEZ\nCIVET\nCIVIL\nCLADE\nCLAIE\nCLAIR\nCLAMA\nCLAME\nCLAMP\nCLAMS\nCLANS\nCLAPA\nCLAPE\nCLAPI\nCLAVA\nCLAVE\nCLEBS\nCLEFS\nCLERC\nCLICS\nCLINS\nCLIPS\nCLIVA\nCLIVE\nCLODO\nCLONE\nCLOPE\nCLORA\nCLORE\nCLOSE\nCLOUA\nCLOUE\nCLOUS\nCLOWN\nCLUBS\nCLUSE\nCOACH\nCOATI\nCOBEA\nCOBOL\nCOBRA\nCOCAS\nCOCHA\nCOCHE\nCOCON\nCOCOS\nCOCUS\nCODAI\nCODAS\nCODAT\nCODEE\nCODER\nCODES\nCODEX\nCODEZ\nCODON\nCOEUR\nCOGNA\nCOGNE\nCOHUE\nCOING\nCOINS\nCOITA\nCOITE\nCOITS\nCOLIN\nCOLIS\nCOLLA\nCOLLE\nCOLON\nCOLTS\nCOLZA\nCOMAS\nCOMBE\nCOMMA\nCOMME\nCOMTE\nCONCU\nCONDE\nCONES\nCONGA\nCONGE\nCONIE\nCONIR\nCONIS\nCONIT\nCONNU\nCONTA\nCONTE\nCOPAL\nCOPIA\nCOPIE\nCOPRA\nCOPTE\nCOQUA\nCOQUE\nCORAN\nCORDA\nCORDE\nCORNA\nCORNE\nCORNU\nCORON\nCORPS\nCORSA\nCORSE\nCOSSA\nCOSSE\nCOSSU\nCOSYS\nCOTAI\nCOTAS\nCOTAT\nCOTEE\nCOTER\nCOTES\nCOTEZ\nCOTIE\nCOTIR\nCOTIS\nCOTIT\nCOTON\nCOTTE\nCOUAC\nCOUDA\nCOUDE\nCOUDS\nCOUIC\nCOULA\nCOULE\nCOUPA\nCOUPE\nCOUPS\nCOURE\nCOURS\nCOURT\nCOURU\nCOUSE\nCOUSU\nCOUTA\nCOUTE\nCOUTS\nCOUVA\nCOUVE\nCOXAL\nCOYAU\nCRABE\nCRACK\nCRADO\nCRAIE\nCRAMA\nCRAME\nCRANA\nCRANE\nCRANS\nCRASE\nCRASH\nCRAVE\nCRAWL\nCREAI\nCREAS\nCREAT\nCREDO\nCREEE\nCREER\nCREES\nCREEZ\nCREMA\nCREME\nCRENA\nCRENE\nCREPA\nCREPE\nCREPI\nCREPU\nCRETE\nCREUX\nCREVA\nCREVE\nCRIAI\nCRIAS\nCRIAT\nCRICS\nCRIEE\nCRIER\nCRIES\nCRIEZ\nCRIME\nCRINS\nCRISE\nCROCS\nCROIE\nCROIS\nCROIT\nCROIX\nCROSS\nCRUEL\nCRUES\nCUBAI\nCUBAS\nCUBAT\nCUBEE\nCUBER\nCUBES\nCUBEZ\nCUIRA\nCUIRE\nCUIRS\nCUISE\nCUITA\nCUITE\nCUITS\nCULAI\nCULAS\nCULAT\nCULEE\nCULER\nCULES\nCULEX\nCULEZ\nCULOT\nCULTE\nCUMIN\nCUMUL\nCURAI\nCURAS\nCURAT\nCUREE\nCURER\nCURES\nCUREZ\nCURRY\nCUVAI\nCUVAS\nCUVAT\nCUVEE\nCUVER\nCUVES\nCUVEZ\nCYANS\nCYCAS\nCYCLE\nCYGNE\nCZARS\nDADAS\nDAGUA\nDAGUE\nDAHIR\nDAIMS\nDAINE\nDALLA\nDALLE\nDALOT\nDAMAI\nDAMAN\nDAMAS\nDAMAT\nDAMEE\nDAMER\nDAMES\nDAMEZ\nDAMNA\nDAMNE\nDANDY\nDANSA\nDANSE\nDARCE\nDARDA\nDARDE\nDARDS\nDARNE\nDARSE\nDATAI\nDATAS\nDATAT\nDATEE\nDATER\nDATES\nDATEZ\nDATIF\nDATTE\nDAUBA\nDAUBE\nDEBAT\nDEBET\nDEBIT\nDEBUT\nDECAN\nDECAS\nDECES\nDECHE\nDECHU\nDECIS\nDECOR\nDECRI\nDECRU\nDECUE\nDECUS\nDECUT\nDEDIA\nDEDIE\nDEDIS\nDEDIT\nDEFIA\nDEFIE\nDEFIS\nDEFIT\nDEGAT\nDEGEL\nDEGRE\nDEITE\nDELAI\nDELCO\nDELIA\nDELIE\nDELIT\nDELOT\nDELTA\nDEMES\nDEMET\nDEMIE\nDEMIS\nDEMIT\nDEMON\nDENIA\nDENIE\nDENIS\nDENSE\nDENTE\nDENTS\nDENUA\nDENUE\nDEPIT\nDEPLU\nDEPOT\nDERBY\nDERME\nDERNY\nDESIR\nDETTE\nDEUIL\nDEVET\nDEVEZ\nDEVIA\nDEVIE\nDEVIN\nDEVIS\nDEVOT\nDEVRA\nDIANE\nDIAPO\nDICOS\nDICTA\nDICTE\nDIESE\nDIETE\nDIEUX\nDIFFA\nDIGIT\nDIGNE\nDIGON\nDIGUE\nDILUA\nDILUE\nDIMES\nDINAI\nDINAR\nDINAS\nDINAT\nDINDE\nDINER\nDINES\nDINEZ\nDINGO\nDIODE\nDIRAI\nDIRAS\nDIREZ\nDISCO\nDISES\nDISSE\nDITES\nDIVAN\nDIVAS\nDIVIN\nDIVIS\nDJAIN\nDJINN\nDOCKS\nDOCTE\nDODOS\nDODUE\nDODUS\nDOGES\nDOGME\nDOGUE\nDOIGT\nDOIVE\nDOLAI\nDOLAS\nDOLAT\nDOLCE\nDOLEE\nDOLER\nDOLES\nDOLEZ\nDOLIC\nDOMES\nDONNA\nDONNE\nDOPAI\nDOPAS\nDOPAT\nDOPEE\nDOPER\nDOPES\nDOPEZ\nDORAI\nDORAS\nDORAT\nDOREE\nDORER\nDORES\nDOREZ\nDORIS\nDORME\nDORMI\nDOSAI\nDOSAS\nDOSAT\nDOSEE\nDOSER\nDOSES\nDOSEZ\nDOSSE\nDOTAI\nDOTAL\nDOTAS\nDOTAT\nDOTEE\nDOTER\nDOTES\nDOTEZ\nDOUAI\nDOUAR\nDOUAS\nDOUAT\nDOUCE\nDOUCI\nDOUEE\nDOUER\nDOUES\nDOUEZ\nDOUMS\nDOURO\nDOUTA\nDOUTE\nDOUVE\nDOUZE\nDOYEN\nDRAIE\nDRAIN\nDRAME\nDRAPA\nDRAPE\nDRAPS\nDRAVE\nDRAYA\nDRAYE\nDREGE\nDRILL\nDRING\nDRINK\nDRIVA\nDRIVE\nDROIT\nDROLE\nDROPA\nDROPE\nDROPS\nDRUES\nDRUPE\nDUALE\nDUAUX\nDUCAL\nDUCAT\nDUCES\nDUCHE\nDUELS\nDUITE\nDUITS\nDULIE\nDUMES\nDUNES\nDUODI\nDUPAI\nDUPAS\nDUPAT\nDUPEE\nDUPER\nDUPES\nDUPEZ\nDURAI\nDURAS\nDURAT\nDURCI\nDUREE\nDURER\nDURES\nDUREZ\nDURIT\nDUSSE\nDUTES\nDUVET\nDYADE\nDZETA\nEBAHI\nEBATS\nEBENE\nEBOUA\nEBOUE\nECALA\nECALE\nECANG\nECART\nECATI\nECHEC\nECHES\nECHOS\nECHUE\nECHUS\nECHUT\nECIMA\nECIME\nECLAT\nECLOS\nECLOT\nECOLE\nECOPA\nECOPE\nECORA\nECORE\nECOTE\nECOTS\nECRAN\nECRIE\nECRIN\nECRIS\nECRIT\nECROU\nECRUE\nECRUS\nECULA\nECULE\nECUMA\nECUME\nECURA\nECURE\nEDAMS\nEDENS\nEDILE\nEDITA\nEDITE\nEDITS\nEFFET\nEGAIE\nEGALA\nEGALE\nEGARA\nEGARD\nEGARE\nEGAUX\nEGAYA\nEGAYE\nEGEEN\nEGIDE\nEGOUT\nEIDER\nELANS\nELAVE\nELBOT\nELEGI\nELEIS\nELEVA\nELEVE\nELFES\nELIDA\nELIDE\nELIMA\nELIME\nELIRA\nELIRE\nELISE\nELITE\nELLES\nELOGE\nELUDA\nELUDE\nELUES\nEMAIL\nEMANA\nEMANE\nEMAUX\nEMBAT\nEMBUA\nEMBUE\nEMBUS\nEMERI\nEMETS\nEMEUS\nEMEUT\nEMIAI\nEMIAS\nEMIAT\nEMIEE\nEMIER\nEMIES\nEMIEZ\nEMIRS\nEMISE\nEMOIS\nEMOUD\nEMOUS\nEMPAN\nEMPLI\nEMUES\nEMULA\nEMULE\nENCAN\nENCAS\nENCRA\nENCRE\nENDOS\nENFER\nENFEU\nENFIN\nENFLA\nENFLE\nENFUI\nENGIN\nENJEU\nENLIA\nENLIE\nENNUI\nENOUA\nENOUE\nENTAI\nENTAS\nENTAT\nENTEE\nENTER\nENTES\nENTEZ\nENTRA\nENTRE\nENVIA\nENVIE\nENVOI\nENVOL\nEPAIR\nEPAIS\nEPALA\nEPALE\nEPAND\nEPARS\nEPATA\nEPATE\nEPAVE\nEPEES\nEPELA\nEPELE\nEPHOD\nEPIAI\nEPIAS\nEPIAT\nEPICA\nEPICE\nEPIEE\nEPIER\nEPIES\nEPIEU\nEPIEZ\nEPIGE\nEPILA\nEPILE\nEPINA\nEPINE\nEPITE\nEPODE\nEPOUX\nEPRIS\nEPUCA\nEPUCE\nEPURA\nEPURE\nEQUIN\nERAIE\nERAYA\nERAYE\nERGOT\nERIGE\nERINE\nERODA\nERODE\nERRAI\nERRAS\nERRAT\nERRER\nERRES\nERREZ\nESCHA\nESCHE\nESCOT\nESPAR\nESSAI\nESSES\nESSOR\nESTER\nESTOC\nETAGE\nETAIE\nETAIN\nETAIS\nETAIT\nETALA\nETALE\nETALS\nETAMA\nETAME\nETANG\nETANT\nETAPE\nETATS\nETAUX\nETAYA\nETAYE\nETEND\nETETA\nETETE\nETEUF\nETHER\nETIER\nETIEZ\nETIRA\nETIRE\nETOCS\nETOLE\nETRES\nETRON\nETUDE\nETUIS\nETUVA\nETUVE\nEUMES\nEUROS\nEUSSE\nEUTES\nEVADE\nEVASA\nEVASE\nEVEIL\nEVENT\nEVIDA\nEVIDE\nEVIER\nEVITA\nEVITE\nEVOHE\nEXACT\nEXCES\nEXCLU\nEXIGE\nEXIGU\nEXILA\nEXILE\nEXILS\nEXODE\nEXPIA\nEXPIE\nEXTRA\nFABLE\nFACES\nFACHA\nFACHE\nFACHO\nFACON\nFACTO\nFADAI\nFADAS\nFADAT\nFADEE\nFADER\nFADES\nFADEZ\nFADOS\nFAGNE\nFAGOT\nFAIMS\nFAINE\nFAIRE\nFAITE\nFAITS\nFAKIR\nFALLU\nFALOT\nFALUN\nFAMEE\nFAMES\nFANAI\nFANAL\nFANAS\nFANAT\nFANEE\nFANER\nFANES\nFANEZ\nFANGE\nFANON\nFAONS\nFARAD\nFARCE\nFARCI\nFARDA\nFARDE\nFARDS\nFAROS\nFARTA\nFARTE\nFARTS\nFASCE\nFASSE\nFASTE\nFATAL\nFATMA\nFATUM\nFAUNE\nFAUTA\nFAUTE\nFAUVE\nFAVUS\nFAXAI\nFAXAS\nFAXAT\nFAXEE\nFAXER\nFAXES\nFAXEZ\nFAYOT\nFEALE\nFEAUX\nFECAL\nFECES\nFEINS\nFEINT\nFELAI\nFELAS\nFELAT\nFELEE\nFELER\nFELES\nFELEZ\nFELIN\nFELON\nFEMME\nFEMUR\nFENDE\nFENDS\nFENDU\nFENIL\nFENTE\nFERAI\nFERAS\nFEREZ\nFERIE\nFERLA\nFERLE\nFERMA\nFERME\nFERRA\nFERRE\nFERRY\nFERUE\nFERUS\nFESSA\nFESSE\nFESSU\nFETAI\nFETAS\nFETAT\nFETEE\nFETER\nFETES\nFETEZ\nFETUS\nFEUES\nFEUIL\nFEULA\nFEULE\nFEVES\nFIAIS\nFIAIT\nFIANT\nFIBRE\nFICHA\nFICHE\nFICHU\nFICUS\nFIEES\nFIEFS\nFIELS\nFIENT\nFIERA\nFIERE\nFIERS\nFIEUX\nFIFRE\nFIGEA\nFIGEE\nFIGER\nFIGES\nFIGEZ\nFIGUE\nFIIEZ\nFILAI\nFILAS\nFILAT\nFILEE\nFILER\nFILES\nFILET\nFILEZ\nFILIN\nFILLE\nFILMA\nFILME\nFILMS\nFILON\nFILOU\nFIMES\nFINAL\nFINES\nFINIE\nFINIR\nFINIS\nFINIT\nFIOLE\nFIONS\nFIRME\nFISCS\nFISSE\nFITES\nFIXAI\nFIXAS\nFIXAT\nFIXEE\nFIXER\nFIXES\nFIXEZ\nFJELD\nFJORD\nFLAIR\nFLANA\nFLANC\nFLANE\nFLANS\nFLAPI\nFLASH\nFLEAU\nFLEIN\nFLEUR\nFLICS\nFLINT\nFLIRT\nFLOOD\nFLOPS\nFLORE\nFLOTS\nFLOUA\nFLOUE\nFLOUS\nFLUAI\nFLUAS\nFLUAT\nFLUER\nFLUES\nFLUET\nFLUEZ\nFLUOR\nFLUSH\nFLUTA\nFLUTE\nFLUXA\nFLUXE\nFOCAL\nFOEHN\nFOIES\nFOINS\nFOIRA\nFOIRE\nFOLIE\nFOLIO\nFOLKS\nFOLLE\nFONCA\nFONCE\nFONDA\nFONDE\nFONDS\nFONDU\nFONTE\nFOOTS\nFORAI\nFORAS\nFORAT\nFORCA\nFORCE\nFORCI\nFOREE\nFORER\nFORES\nFORET\nFOREZ\nFORGE\nFORMA\nFORME\nFORTE\nFORTS\nFORUM\nFOSSE\nFOUEE\nFOUET\nFOUGE\nFOUIE\nFOUIR\nFOUIS\nFOUIT\nFOULA\nFOULE\nFOURS\nFOUTE\nFOUTU\nFOVEA\nFOXEE\nFOXES\nFOYER\nFRACS\nFRAIE\nFRAIS\nFRANC\nFRAPE\nFRAYA\nFRAYE\nFREIN\nFRELE\nFREMI\nFRENE\nFREON\nFRERE\nFRETA\nFRETE\nFRETS\nFREUX\nFRICS\nFRIGO\nFRIMA\nFRIME\nFRIPA\nFRIPE\nFRIRA\nFRIRE\nFRISA\nFRISE\nFRITE\nFRITZ\nFROCS\nFROID\nFROLA\nFROLE\nFRONT\nFROUA\nFROUE\nFRUIT\nFUCUS\nFUELS\nFUGUA\nFUGUE\nFUIES\nFUIRA\nFUITE\nFULLS\nFUMAI\nFUMAS\nFUMAT\nFUMEE\nFUMER\nFUMES\nFUMET\nFUMEZ\nFUNIN\nFUNKY\nFURAX\nFURET\nFURIA\nFURIE\nFUSAI\nFUSAS\nFUSAT\nFUSEE\nFUSEL\nFUSER\nFUSES\nFUSEZ\nFUSIL\nFUSSE\nFUTEE\nFUTES\nFUTUR\nFUYEZ\nGABLE\nGACHA\nGACHE\nGADES\nGADIN\nGAFFA\nGAFFE\nGAGAS\nGAGEA\nGAGEE\nGAGER\nGAGES\nGAGEZ\nGAGNA\nGAGNE\nGAIAC\nGAIES\nGAINA\nGAINE\nGAINS\nGALAS\nGALBA\nGALBE\nGALES\nGALET\nGALLO\nGALON\nGALOP\nGAMBA\nGAMBE\nGAMIN\nGAMME\nGANGA\nGANGS\nGANSA\nGANSE\nGANTA\nGANTE\nGANTS\nGARAI\nGARAS\nGARAT\nGARCE\nGARDA\nGARDE\nGAREE\nGARER\nGARES\nGAREZ\nGARNI\nGAROU\nGATAI\nGATAS\nGATAT\nGATEE\nGATER\nGATES\nGATEZ\nGATTE\nGAUDE\nGAULA\nGAULE\nGAUPE\nGAURS\nGAUSS\nGAVAI\nGAVAS\nGAVAT\nGAVEE\nGAVER\nGAVES\nGAVEZ\nGAYAL\nGAZAI\nGAZAS\nGAZAT\nGAZEE\nGAZER\nGAZES\nGAZEZ\nGAZON\nGEAIS\nGEANT\nGECKO\nGEINS\nGEINT\nGELAI\nGELAS\nGELAT\nGELEE\nGELER\nGELES\nGELEZ\nGELIF\nGEMIE\nGEMIR\nGEMIS\nGEMIT\nGEMMA\nGEMME\nGENAI\nGENAS\nGENAT\nGENEE\nGENER\nGENES\nGENET\nGENEZ\nGENIE\nGENOU\nGENRE\nGEODE\nGEOLE\nGERAI\nGERAS\nGERAT\nGERBA\nGERBE\nGERCA\nGERCE\nGEREE\nGERER\nGERES\nGEREZ\nGERMA\nGERME\nGESIR\nGESSE\nGESTE\nGIBET\nGIBUS\nGICLA\nGICLE\nGIFLA\nGIFLE\nGIGOT\nGIGUE\nGILDE\nGILET\nGILLE\nGIRIE\nGIRLS\nGIRON\nGISEZ\nGITAI\nGITAN\nGITAS\nGITAT\nGITER\nGITES\nGITEZ\nGITON\nGIVRA\nGIVRE\nGLACA\nGLACE\nGLANA\nGLAND\nGLANE\nGLAPI\nGLASS\nGLATI\nGLEBE\nGLENE\nGLOBE\nGLOME\nGLOSA\nGLOSE\nGLUAU\nGLUIS\nGLUME\nGNOLE\nGNOME\nGNONS\nGNOSE\nGNOUS\nGOALS\nGOBAI\nGOBAS\nGOBAT\nGOBEE\nGOBER\nGOBES\nGOBEZ\nGOBIE\nGODAI\nGODAS\nGODAT\nGODER\nGODES\nGODET\nGODEZ\nGOGLU\nGOGOS\nGOLFE\nGOLFS\nGOMBO\nGOMMA\nGOMME\nGONDA\nGONDE\nGONDS\nGONGS\nGONZE\nGORDS\nGORET\nGORGE\nGOSSE\nGOTON\nGOUDA\nGOUET\nGOUGE\nGOULE\nGOULU\nGOUMS\nGOURA\nGOURD\nGOURE\nGOUTA\nGOUTE\nGOUTS\nGOYIM\nGRACE\nGRADE\nGRAIN\nGRAND\nGRAUX\nGRAVA\nGRAVE\nGRAVI\nGREAI\nGREAS\nGREAT\nGREBE\nGRECS\nGREEE\nGREEN\nGREER\nGREES\nGREEZ\nGREGE\nGRELA\nGRELE\nGRENA\nGRENE\nGRENU\nGRESA\nGRESE\nGREVA\nGREVE\nGRIEF\nGRILL\nGRILS\nGRIMA\nGRIME\nGRIOT\nGRISA\nGRISE\nGRIVE\nGROGS\nGROIN\nGROLE\nGROOM\nGROUP\nGRUAU\nGRUES\nGRUGE\nGRUME\nGUAIS\nGUANO\nGUEAI\nGUEAS\nGUEAT\nGUEDE\nGUEEE\nGUEER\nGUEES\nGUEEZ\nGUEPE\nGUERE\nGUERI\nGUETE\nGUETS\nGUEUX\nGUIDA\nGUIDE\nGUIPA\nGUIPE\nGUISE\nGUPPY\nGURUS\nGUZLA\nGYPSE\nGYRIN\nHABIT\nHABLA\nHABLE\nHACHA\nHACHE\nHADJI\nHAIES\nHAIKS\nHAIKU\nHAINE\nHAIRA\nHAIRE\nHALAI\nHALAS\nHALAT\nHALBI\nHALEE\nHALER\nHALES\nHALEZ\nHALLE\nHALLS\nHALOS\nHALTE\nHALVA\nHAMAC\nHAMPE\nHANAP\nHANSE\nHANTA\nHANTE\nHAPAX\nHAPPA\nHAPPE\nHARAS\nHARDA\nHARDE\nHARDI\nHAREM\nHARKI\nHARLE\nHAROS\nHARPA\nHARPE\nHARTS\nHASCH\nHASES\nHASTE\nHASTS\nHATAI\nHATAS\nHATAT\nHATEE\nHATER\nHATES\nHATEZ\nHATIF\nHAUTE\nHAUTS\nHAVAI\nHAVAS\nHAVAT\nHAVEE\nHAVER\nHAVES\nHAVEZ\nHAVIE\nHAVIR\nHAVIS\nHAVIT\nHAVRE\nHAYON\nHECTO\nHELAI\nHELAS\nHELAT\nHELEE\nHELER\nHELES\nHELEZ\nHELIX\nHELLO\nHENNE\nHENNI\nHENRY\nHERBA\nHERBE\nHERBU\nHERES\nHERON\nHEROS\nHERPE\nHERSA\nHERSE\nHERTZ\nHETRE\nHEURE\nHEURS\nHEURT\nHEVEA\nHIBOU\nHINDI\nHIPPY\nHISSA\nHISSE\nHIVER\nHOBBY\nHOCCO\nHOCHA\nHOCHE\nHOIRS\nHOMES\nHOMME\nHOMOS\nHONNI\nHONTE\nHORDE\nHORST\nHOSTO\nHOTEL\nHOTES\nHOTTE\nHOUAI\nHOUAS\nHOUAT\nHOUEE\nHOUER\nHOUES\nHOUEZ\nHOUKA\nHOULE\nHOURD\nHOURI\nHOYAU\nHUAIS\nHUAIT\nHUANT\nHUARD\nHUART\nHUCHA\nHUCHE\nHUEES\nHUENT\nHUERA\nHUIEZ\nHUILA\nHUILE\nHUMAI\nHUMAS\nHUMAT\nHUMEE\nHUMER\nHUMES\nHUMEZ\nHUMUS\nHUNES\nHUONS\nHUPPE\nHURES\nHURLA\nHURLE\nHURON\nHUTTE\nHYDNE\nHYDRE\nHYENE\nHYMEN\nHYMNE\nIAMBE\nIBERE\nICHOR\nICONE\nICTUS\nIDEAL\nIDEEL\nIDEES\nIDIOT\nIDOLE\nIGLOO\nIGNEE\nIGNES\nIGUES\nILEAL\nILEON\nILEUS\nILIEN\nILION\nILOTE\nILOTS\nIMAGE\nIMAGO\nIMAMS\nIMBUE\nIMBUS\nIMIDE\nIMITA\nIMITE\nIMMUN\nIMPER\nIMPIE\nIMPOT\nIMPUR\nINCAS\nINDEX\nINDUE\nINDUS\nINFRA\nINFUS\nINLAY\nINNEE\nINNES\nINOUI\nINPUT\nINTER\nINTIS\nINUIT\nINULE\nIODAI\nIODAS\nIODAT\nIODEE\nIODER\nIODES\nIODEZ\nIODLA\nIODLE\nIOULA\nIOULE\nIPECA\nIRAIS\nIRAIT\nIRIEZ\nIRISA\nIRISE\nIRONE\nIRONS\nIRONT\nISARD\nISBAS\nISLAM\nISOLA\nISOLE\nISSUE\nISSUS\nITEMS\nITERA\nITERE\nIULES\nIVRES\nIXIAS\nIXODE\nJABLA\nJABLE\nJABOT\nJACEE\nJACKS\nJACOT\nJACTA\nJACTE\nJADES\nJADIS\nJAINA\nJAINS\nJALAP\nJALES\nJALON\nJAMBE\nJANTE\nJAPON\nJAPPA\nJAPPE\nJARDE\nJARRE\nJASAI\nJASAS\nJASAT\nJASER\nJASES\nJASEZ\nJASPA\nJASPE\nJATTE\nJAUGE\nJAUNE\nJAUNI\nJAVAS\nJAVEL\nJEANS\nJEEPS\nJENNY\nJEREZ\nJERKA\nJERKE\nJERKS\nJESUS\nJETAI\nJETAS\nJETAT\nJETEE\nJETER\nJETES\nJETEZ\nJETON\nJETTE\nJEUDI\nJEUNA\nJEUNE\nJODLA\nJODLE\nJOIES\nJOINS\nJOINT\nJOKER\nJOLIE\nJOLIS\nJONCA\nJONCE\nJONCS\nJOTAS\nJOUAI\nJOUAL\nJOUAS\nJOUAT\nJOUEE\nJOUER\nJOUES\nJOUET\nJOUEZ\nJOUGS\nJOUIR\nJOUIS\nJOUIT\nJOULE\nJOURS\nJOUTA\nJOUTE\nJOYAU\nJUBES\nJUCHA\nJUCHE\nJUDAS\nJUDOS\nJUGAL\nJUGEA\nJUGEE\nJUGER\nJUGES\nJUGEZ\nJUIFS\nJUILL\nJUIVE\nJULEP\nJULES\nJUMBO\nJUMEL\nJUNTE\nJUPES\nJUPON\nJURAI\nJURAS\nJURAT\nJUREE\nJURER\nJURES\nJUREZ\nJURON\nJURYS\nJUSEE\nJUSQU\nJUSTE\nJUTAI\nJUTAS\nJUTAT\nJUTEE\nJUTER\nJUTES\nJUTEZ\nKACHA\nKACHE\nKAKIS\nKALIS\nKAMIS\nKAPOK\nKAPPA\nKARMA\nKARTS\nKAVAS\nKAWAS\nKAYAC\nKAYAK\nKEFIR\nKENDO\nKEPIS\nKETCH\nKHANS\nKHATS\nKHMER\nKHOLS\nKICKS\nKIEFS\nKIKIS\nKILOS\nKILTS\nKIWIS\nKNOUT\nKOALA\nKOINE\nKOLAS\nKORES\nKRAAL\nKRACH\nKRAFT\nKRAKS\nKRISS\nKSOUR\nKURDE\nKYRIE\nKYSTE\nLABEL\nLABIE\nLABRE\nLACAI\nLACAS\nLACAT\nLACEE\nLACER\nLACES\nLACET\nLACEZ\nLACHA\nLACHE\nLACIS\nLACTE\nLADIN\nLADRE\nLAGON\nLAICS\nLAIDE\nLAIDS\nLAIES\nLAINA\nLAINE\nLAIRD\nLAITE\nLAITS\nLAIUS\nLAIZE\nLAMAI\nLAMAS\nLAMAT\nLAMEE\nLAMER\nLAMES\nLAMEZ\nLAMIE\nLAMPA\nLAMPE\nLANCA\nLANCE\nLANDE\nLANGE\nLAPAI\nLAPAS\nLAPAT\nLAPEE\nLAPER\nLAPES\nLAPEZ\nLAPIN\nLAPIS\nLAQUA\nLAQUE\nLARDA\nLARDE\nLARDS\nLARGE\nLARGO\nLARME\nLARVE\nLASER\nLASSA\nLASSE\nLASSO\nLATEX\nLATIN\nLATTA\nLATTE\nLAURE\nLAVAI\nLAVAS\nLAVAT\nLAVEE\nLAVER\nLAVES\nLAVEZ\nLAVIS\nLAYAI\nLAYAS\nLAYAT\nLAYEE\nLAYER\nLAYES\nLAYEZ\nLAYON\nLAZZI\nLEBEL\nLECHA\nLECHE\nLECON\nLEDIT\nLEGAL\nLEGAT\nLEGER\nLEGES\nLEGUA\nLEGUE\nLEMME\nLENTE\nLENTO\nLENTS\nLEPRE\nLEROT\nLESAI\nLESAS\nLESAT\nLESEE\nLESER\nLESES\nLESEZ\nLESTA\nLESTE\nLESTS\nLETAL\nLEUDE\nLEURS\nLEVAI\nLEVAS\nLEVAT\nLEVEE\nLEVER\nLEVES\nLEVEZ\nLEVRE\nLEXIE\nLEXIS\nLIAGE\nLIAIS\nLIAIT\nLIANE\nLIANT\nLIARD\nLIBER\nLIBRE\nLICES\nLICHA\nLICHE\nLICOL\nLICOU\nLIDOS\nLIEDS\nLIEES\nLIEGE\nLIENS\nLIENT\nLIERA\nLIEUE\nLIEUR\nLIEUS\nLIEUX\nLIFTA\nLIFTE\nLIFTS\nLIGES\nLIGIE\nLIGNA\nLIGNE\nLIGOT\nLIGUA\nLIGUE\nLIIEZ\nLILAS\nLIMAI\nLIMAN\nLIMAS\nLIMAT\nLIMBE\nLIMEE\nLIMER\nLIMES\nLIMEZ\nLIMON\nLINER\nLINGA\nLINGE\nLINKS\nLINON\nLINOS\nLIONS\nLIPPE\nLIPPU\nLIRAI\nLIRAS\nLIREZ\nLIRON\nLISES\nLISEZ\nLISSA\nLISSE\nLISTA\nLISTE\nLITAI\nLITAS\nLITAT\nLITEE\nLITER\nLITES\nLITEZ\nLITHO\nLITRE\nLIURE\nLIVES\nLIVET\nLIVRA\nLIVRE\nLOBAI\nLOBAS\nLOBAT\nLOBBY\nLOBEE\nLOBER\nLOBES\nLOBEZ\nLOCAL\nLOCHA\nLOCHE\nLOCHS\nLODEN\nLOESS\nLOFAI\nLOFAS\nLOFAT\nLOFER\nLOFES\nLOFEZ\nLOFTS\nLOGEA\nLOGEE\nLOGER\nLOGES\nLOGEZ\nLOGIS\nLOGOS\nLOIRS\nLOLOS\nLONGE\nLONGS\nLOOCH\nLOOFA\nLOOKS\nLOPIN\nLOQUA\nLOQUE\nLORAN\nLORDS\nLORIS\nLOTES\nLOTIE\nLOTIR\nLOTIS\nLOTIT\nLOTOS\nLOTTE\nLOTUS\nLOUAI\nLOUAS\nLOUAT\nLOUEE\nLOUER\nLOUES\nLOUEZ\nLOUFA\nLOUFE\nLOUIS\nLOUPA\nLOUPE\nLOUPS\nLOURA\nLOURD\nLOURE\nLOUVA\nLOUVE\nLOVAI\nLOVAS\nLOVAT\nLOVEE\nLOVER\nLOVES\nLOVEZ\nLOYAL\nLOYER\nLUBIE\nLUCRE\nLUEUR\nLUFFA\nLUGEA\nLUGER\nLUGES\nLUGEZ\nLUIRA\nLUIRE\nLUISE\nLUITE\nLUITS\nLULUS\nLUMEN\nLUMES\nLUMPS\nLUNCH\nLUNDI\nLUNEE\nLUNES\nLUPIN\nLUPUS\nLURON\nLUSIN\nLUSSE\nLUTAI\nLUTAS\nLUTAT\nLUTEE\nLUTER\nLUTES\nLUTEZ\nLUTHS\nLUTIN\nLUTTA\nLUTTE\nLUXAI\nLUXAS\nLUXAT\nLUXEE\nLUXER\nLUXES\nLUXEZ\nLYCEE\nLYCRA\nLYRES\nLYRIC\nLYSAI\nLYSAS\nLYSAT\nLYSEE\nLYSER\nLYSES\nLYSEZ\nMACHA\nMACHE\nMACHO\nMACIS\nMACLA\nMACLE\nMACON\nMACRE\nMADRE\nMAFIA\nMAGES\nMAGIE\nMAGMA\nMAGNA\nMAGNE\nMAGOT\nMAIAS\nMAIES\nMAILS\nMAINS\nMAINT\nMAIRE\nMAJOR\nMAKIS\nMALES\nMALIN\nMALIS\nMALLE\nMALTA\nMALTE\nMALTS\nMALUS\nMAMAN\nMAMBO\nMAMIE\nMAMMY\nMANAS\nMANDA\nMANDE\nMANES\nMANGE\nMANIA\nMANIE\nMANNE\nMANSE\nMANTE\nMAORI\nMAOUX\nMAQUA\nMAQUE\nMARCS\nMARDI\nMAREE\nMARES\nMARGE\nMARIA\nMARIE\nMARIN\nMARIS\nMARKS\nMARLI\nMARNA\nMARNE\nMARRA\nMARRE\nMARRI\nMARTE\nMASER\nMASSA\nMASSE\nMATAF\nMATAI\nMATAS\nMATAT\nMATCH\nMATEE\nMATER\nMATES\nMATEZ\nMATHS\nMATIE\nMATIN\nMATIR\nMATIS\nMATIT\nMATON\nMATOU\nMATTE\nMAURE\nMAUVE\nMAYAS\nMAYEN\nMAYES\nMEATS\nMECHA\nMECHE\nMEDES\nMEDIA\nMEDIS\nMEDIT\nMEDOC\nMEFIA\nMEFIE\nMEFIS\nMEFIT\nMEGIE\nMEGIR\nMEGIS\nMEGIT\nMEGOT\nMEIJI\nMELAI\nMELAS\nMELAT\nMELBA\nMELEE\nMELER\nMELES\nMELEZ\nMELIA\nMELON\nMELOS\nMEMES\nMENAI\nMENAS\nMENAT\nMENEE\nMENER\nMENES\nMENEZ\nMENIN\nMENSE\nMENTE\nMENTI\nMENUE\nMENUS\nMERCI\nMERDE\nMERES\nMERLE\nMERLU\nMEROU\nMESAS\nMESSE\nMETAL\nMETAS\nMETEO\nMETIS\nMETRA\nMETRE\nMETRO\nMETTE\nMEULA\nMEULE\nMEURE\nMEURS\nMEURT\nMEUTE\nMEUVE\nMEZZO\nMIAOU\nMICAS\nMICHE\nMICRO\nMIDIS\nMIELS\nMIENS\nMIEUX\nMIGRA\nMIGRE\nMILAN\nMILES\nMILLE\nMIMAI\nMIMAS\nMIMAT\nMIMEE\nMIMER\nMIMES\nMIMEZ\nMIMIS\nMINAI\nMINAS\nMINAT\nMINCE\nMINCI\nMINEE\nMINER\nMINES\nMINET\nMINEZ\nMINIS\nMINOT\nMINOU\nMINUS\nMIRAI\nMIRAS\nMIRAT\nMIREE\nMIRER\nMIRES\nMIREZ\nMIROS\nMISAI\nMISAS\nMISAT\nMISEE\nMISER\nMISES\nMISEZ\nMISSE\nMITAI\nMITAN\nMITAS\nMITAT\nMITEE\nMITER\nMITES\nMITEZ\nMITON\nMITRE\nMIXAI\nMIXAS\nMIXAT\nMIXEE\nMIXER\nMIXES\nMIXEZ\nMIXTE\nMOCHE\nMOCOS\nMODAL\nMODEM\nMODES\nMODUS\nMOERE\nMOINE\nMOINS\nMOIRA\nMOIRE\nMOISA\nMOISE\nMOISI\nMOITA\nMOITE\nMOITI\nMOKAS\nMOLES\nMOLLE\nMOLLI\nMOLLO\nMOLYS\nMOMES\nMOMIE\nMONDA\nMONDE\nMONEL\nMONOS\nMONTA\nMONTE\nMONTS\nMOQUA\nMOQUE\nMORAL\nMORDE\nMORDS\nMORDU\nMORES\nMORIO\nMORNE\nMORSE\nMORTE\nMORTS\nMORUE\nMORVE\nMOSAN\nMOTEL\nMOTET\nMOTIF\nMOTOS\nMOTTA\nMOTTE\nMOTUS\nMOUDS\nMOUES\nMOULA\nMOULE\nMOULT\nMOULU\nMOUTS\nMOUVA\nMOUVE\nMOXAS\nMOYEE\nMOYEN\nMOYES\nMOYEU\nMUAIS\nMUAIT\nMUANT\nMUCHA\nMUCHE\nMUCOR\nMUCUS\nMUEES\nMUENT\nMUERA\nMUETS\nMUFLE\nMUFTI\nMUGES\nMUGIR\nMUGIS\nMUGIT\nMUIDS\nMUIEZ\nMULES\nMULET\nMULON\nMULOT\nMUMES\nMUNIE\nMUNIR\nMUNIS\nMUNIT\nMUONS\nMURAI\nMURAL\nMURAS\nMURAT\nMUREE\nMURER\nMURES\nMURET\nMUREX\nMUREZ\nMURIE\nMURIR\nMURIS\nMURIT\nMUSAI\nMUSAS\nMUSAT\nMUSCS\nMUSEE\nMUSER\nMUSES\nMUSEZ\nMUSSA\nMUSSE\nMUSTS\nMUTAI\nMUTAS\nMUTAT\nMUTEE\nMUTER\nMUTES\nMUTEZ\nMUTIN\nMYOME\nMYOPE\nMYRTE\nMYTHE\nNABAB\nNABOT\nNACRA\nNACRE\nNADIR\nNAFES\nNAGEA\nNAGEE\nNAGER\nNAGES\nNAGEZ\nNAIFS\nNAINE\nNAINS\nNAIVE\nNAJAS\nNANAN\nNANAR\nNANAS\nNANTI\nNAPEE\nNAPEL\nNAPPA\nNAPPE\nNARDS\nNARRA\nNARRE\nNASAL\nNASES\nNASSE\nNATAL\nNATIF\nNATTA\nNATTE\nNAVAL\nNAVET\nNAVRA\nNAVRE\nNAZIE\nNAZIS\nNEANT\nNEBKA\nNECKS\nNEFLE\nNEGRE\nNEGUS\nNEIGE\nNENES\nNENNI\nNEONS\nNERFS\nNERVI\nNETTE\nNEUFS\nNEUME\nNEUVE\nNEVES\nNEVEU\nNIAIS\nNIAIT\nNIANT\nNICHA\nNICHE\nNICOL\nNIECE\nNIEES\nNIEME\nNIENT\nNIERA\nNIFES\nNIIEZ\nNILLE\nNIMBA\nNIMBE\nNINAS\nNIOLE\nNIONS\nNIPPA\nNIPPE\nNIQUE\nNITRA\nNITRE\nNIVAL\nNIXES\nNOBLE\nNOCES\nNOCIF\nNOELS\nNOEUD\nNOIES\nNOIRE\nNOIRS\nNOISE\nNOLIS\nNOMES\nNOMMA\nNOMME\nNONCE\nNONES\nNONNE\nNOPAI\nNOPAL\nNOPAS\nNOPAT\nNOPEE\nNOPER\nNOPES\nNOPEZ\nNORDI\nNORIA\nNORME\nNOTAI\nNOTAS\nNOTAT\nNOTEE\nNOTER\nNOTES\nNOTEZ\nNOTRE\nNOUAI\nNOUAS\nNOUAT\nNOUBA\nNOUEE\nNOUER\nNOUES\nNOUEZ\nNOVAI\nNOVAS\nNOVAT\nNOVEE\nNOVER\nNOVES\nNOVEZ\nNOYAI\nNOYAS\nNOYAT\nNOYAU\nNOYEE\nNOYER\nNOYES\nNOYEZ\nNUAGE\nNUAIS\nNUAIT\nNUANT\nNUEES\nNUENT\nNUERA\nNUIEZ\nNUIRA\nNUIRE\nNUISE\nNUITE\nNUITS\nNULLE\nNUONS\nNUQUE\nNURSE\nNYLON\nOASIS\nOBEIR\nOBEIS\nOBEIT\nOBELE\nOBELS\nOBERA\nOBERE\nOBESE\nOBIER\nOBITS\nOBJET\nOBLAT\nOBOLE\nOBTUS\nOBVIA\nOBVIE\nOCCLU\nOCEAN\nOCRAI\nOCRAS\nOCRAT\nOCREE\nOCRER\nOCRES\nOCREZ\nOCTET\nODEON\nODEUR\nOEUFS\nOEUVE\nOFFRE\nOFLAG\nOGIVE\nOGRES\nOILLE\nOINGS\nOINTE\nOINTS\nOISIF\nOISON\nOKAPI\nOLEUM\nOLIVE\nOMBLE\nOMBRA\nOMBRE\nOMEGA\nOMETS\nOMISE\nONCES\nONCLE\nONDEE\nONDES\nONDIN\nONGLE\nOPALE\nOPENS\nOPERA\nOPERE\nOPIAT\nOPINA\nOPINE\nOPIUM\nOPTAI\nOPTAS\nOPTAT\nOPTER\nOPTES\nOPTEZ\nORAGE\nORALE\nORANT\nORAUX\nORBES\nORDRE\nOREES\nORGES\nORGIE\nORGUE\nORIEL\nORINS\nORLES\nORLON\nORMES\nORNAI\nORNAS\nORNAT\nORNEE\nORNER\nORNES\nORNEZ\nOROBE\nORPIN\nORQUE\nORTIE\nORVET\nOSAIS\nOSAIT\nOSANT\nOSCAR\nOSEES\nOSENT\nOSERA\nOSIDE\nOSIER\nOSIEZ\nOSONS\nOSQUE\nOSSUE\nOSSUS\nOTAGE\nOTAIS\nOTAIT\nOTANT\nOTEES\nOTENT\nOTERA\nOTIEZ\nOTITE\nOTONS\nOUAIS\nOUATA\nOUATE\nOUBLI\nOUCHE\nOUEDS\nOUEST\nOUIES\nOUIRA\nOURDI\nOURLA\nOURLE\nOURSE\nOUSTE\nOUTIL\nOUTRA\nOUTRE\nOUVRA\nOUVRE\nOVALE\nOVATE\nOVINE\nOVINS\nOVNIS\nOVULA\nOVULE\nOXYDA\nOXYDE\nOYAIS\nOYATS\nOZENE\nOZONE\nPACHA\nPACKS\nPACTE\nPADDY\nPADOU\nPAGEA\nPAGEE\nPAGEL\nPAGER\nPAGES\nPAGEZ\nPAGNE\nPAGRE\nPAGUS\nPAIEN\nPAIES\nPAINS\nPAIRE\nPAIRS\nPALAN\nPALEE\nPALES\nPALET\nPALIE\nPALIR\nPALIS\nPALIT\nPALMA\nPALME\nPALOT\nPALPA\nPALPE\nPALUD\nPALUS\nPAMAI\nPAMAS\nPAMAT\nPAMEE\nPAMER\nPAMES\nPAMEZ\nPAMPA\nPANAI\nPANAS\nPANAT\nPANAX\nPANCA\nPANDA\nPANEE\nPANEL\nPANER\nPANES\nPANEZ\nPANIC\nPANKA\nPANNA\nPANNE\nPANSA\nPANSE\nPANSU\nPAONS\nPAPAL\nPAPAS\nPAPES\nPAPIS\nPAPYS\nPAQUE\nPARAI\nPARAS\nPARAT\nPARCE\nPARCS\nPARDI\nPAREE\nPAREO\nPARER\nPARES\nPAREZ\nPARIA\nPARIE\nPARIS\nPARKA\nPARLA\nPARLE\nPARME\nPARMI\nPAROI\nPAROS\nPARSI\nPARTE\nPARTI\nPARTS\nPARUE\nPARUS\nPARUT\nPASSA\nPASSE\nPATAI\nPATAS\nPATAT\nPATEE\nPATER\nPATES\nPATEZ\nPATIO\nPATIR\nPATIS\nPATIT\nPATON\nPATRE\nPATTA\nPATTE\nPATTU\nPAUMA\nPAUME\nPAUSA\nPAUSE\nPAVAI\nPAVAS\nPAVAT\nPAVEE\nPAVER\nPAVES\nPAVEZ\nPAVIE\nPAVOT\nPAYAI\nPAYAS\nPAYAT\nPAYEE\nPAYER\nPAYES\nPAYEZ\nPAYSE\nPEAGE\nPEANS\nPEAUX\nPECHA\nPECHE\nPEDUM\nPEGRE\nPEINA\nPEINE\nPEINS\nPEINT\nPEKAN\nPEKIN\nPELAI\nPELAS\nPELAT\nPELEE\nPELER\nPELES\nPELEZ\nPELLE\nPELTA\nPELTE\nPENAL\nPENDE\nPENDS\nPENDU\nPENES\nPENIL\nPENIS\nPENNE\nPENNY\nPENON\nPENSA\nPENSE\nPENTE\nPENTU\nPEONS\nPEPES\nPEPIA\nPEPIE\nPEPIN\nPEPON\nPERCA\nPERCE\nPERCU\nPERDE\nPERDS\nPERDU\nPERES\nPERIL\nPERIR\nPERIS\nPERIT\nPERLA\nPERLE\nPEROT\nPERSE\nPERTE\nPESAI\nPESAS\nPESAT\nPESEE\nPESER\nPESES\nPESEZ\nPESON\nPESOS\nPESSE\nPESTA\nPESTE\nPETAI\nPETAS\nPETAT\nPETEE\nPETER\nPETES\nPETEZ\nPETIT\nPETON\nPETRE\nPETRI\nPETUN\nPEULS\nPEURS\nPEZES\nPHAGE\nPHARE\nPHASE\nPHILO\nPHLOX\nPHONE\nPHONO\nPHOTO\nPIAFS\nPIANO\nPIANS\nPICAS\nPICOT\nPIECE\nPIEDS\nPIEGE\nPIETA\nPIETE\nPIEUX\nPIEZE\nPIFAI\nPIFAS\nPIFAT\nPIFEE\nPIFER\nPIFES\nPIFEZ\nPIFFA\nPIFFE\nPIGEA\nPIGEE\nPIGER\nPIGES\nPIGEZ\nPIGNE\nPILAF\nPILAI\nPILAS\nPILAT\nPILEE\nPILER\nPILES\nPILET\nPILEZ\nPILLA\nPILLE\nPILON\nPILOU\nPILUM\nPINCA\nPINCE\nPINNE\nPINOT\nPINTA\nPINTE\nPIONS\nPIPAI\nPIPAS\nPIPAT\nPIPEE\nPIPER\nPIPES\nPIPEZ\nPIPIS\nPIPIT\nPIQUA\nPIQUE\nPIRES\nPISES\nPISSA\nPISSE\nPISTA\nPISTE\nPITES\nPITIE\nPITON\nPITRE\nPIVES\nPIVOT\nPIZZA\nPLACA\nPLACE\nPLAGE\nPLAID\nPLAIE\nPLAIS\nPLAIT\nPLANA\nPLANE\nPLANS\nPLANT\nPLATE\nPLATS\nPLEBE\nPLEIN\nPLEUR\nPLEUT\nPLIAI\nPLIAS\nPLIAT\nPLIEE\nPLIER\nPLIES\nPLIEZ\nPLOIE\nPLOMB\nPLOTS\nPLOUC\nPLOUF\nPLOUK\nPLOYA\nPLOYE\nPLUIE\nPLUMA\nPLUME\nPNEUS\nPOCHA\nPOCHE\nPOELA\nPOELE\nPOEME\nPOETE\nPOGNE\nPOIDS\nPOILA\nPOILE\nPOILS\nPOILU\nPOING\nPOINS\nPOINT\nPOIRE\nPOISE\nPOKER\nPOLAR\nPOLES\nPOLIE\nPOLIO\nPOLIR\nPOLIS\nPOLIT\nPOLKA\nPOLOS\nPOMMA\nPOMME\nPOMPA\nPOMPE\nPONCA\nPONCE\nPONDE\nPONDS\nPONDU\nPONEY\nPONGE\nPONTA\nPONTE\nPONTS\nPOOLS\nPOPES\nPOQUA\nPOQUE\nPORCS\nPORES\nPORNO\nPORTA\nPORTE\nPORTO\nPORTS\nPOSAI\nPOSAS\nPOSAT\nPOSEE\nPOSER\nPOSES\nPOSEZ\nPOSTA\nPOSTE\nPOTEE\nPOTES\nPOTIN\nPOUAH\nPOUCE\nPOUFS\nPOULE\nPOULS\nPOUPE\nPRAME\nPREAU\nPRELE\nPREND\nPRETA\nPRETE\nPRETS\nPREUX\nPREVU\nPRIAI\nPRIAS\nPRIAT\nPRIEE\nPRIER\nPRIES\nPRIEZ\nPRIMA\nPRIME\nPRIMO\nPRISA\nPRISE\nPRIVA\nPRIVE\nPROBE\nPROFS\nPROIE\nPROLO\nPROMO\nPROMU\nPRONA\nPRONE\nPROSE\nPROTE\nPROUE\nPROVO\nPRUDE\nPRUNE\nPSITT\nPSOAS\nPTOSE\nPUAIS\nPUAIT\nPUANT\nPUBIS\nPUCES\nPUCHE\nPUEES\nPUENT\nPUERA\nPUIEZ\nPUINE\nPUISA\nPUISE\nPUITS\nPULLS\nPULPE\nPULSA\nPULSE\nPUMAS\nPUMES\nPUNAS\nPUNCH\nPUNIE\nPUNIR\nPUNIS\nPUNIT\nPUNKS\nPUONS\nPUPES\nPUREE\nPURES\nPURGE\nPURIN\nPUROT\nPUSSE\nPUTES\nPUTTI\nPUTTO\nPYREX\nQUAIS\nQUAND\nQUANT\nQUARK\nQUART\nQUASI\nQUELS\nQUETA\nQUETE\nQUEUE\nQUEUX\nQUIET\nQUINE\nQUINT\nQUIPO\nQUIPU\nQUOTA\nRABAN\nRABAT\nRABBI\nRABLA\nRABLE\nRABOT\nRACEE\nRACER\nRACES\nRACLA\nRACLE\nRADAI\nRADAR\nRADAS\nRADAT\nRADEE\nRADER\nRADES\nRADEZ\nRADIA\nRADIE\nRADIN\nRADIO\nRADIS\nRAFLA\nRAFLE\nRAGEA\nRAGER\nRAGES\nRAGEZ\nRAGOT\nRAGUA\nRAGUE\nRAIAS\nRAIDE\nRAIDI\nRAIDS\nRAIES\nRAILS\nRAINA\nRAINE\nRAJAH\nRAJAS\nRAKIS\nRALAI\nRALAS\nRALAT\nRALER\nRALES\nRALEZ\nRAMAI\nRAMAS\nRAMAT\nRAMEE\nRAMER\nRAMES\nRAMEZ\nRAMIE\nRAMIS\nRAMPA\nRAMPE\nRANCE\nRANCH\nRANCI\nRANGE\nRANGS\nRANIS\nRAOUT\nRAPAI\nRAPAS\nRAPAT\nRAPEE\nRAPER\nRAPES\nRAPEZ\nRAPIN\nRAPTS\nRAQUA\nRAQUE\nRARES\nRASAI\nRASAS\nRASAT\nRASEE\nRASER\nRASES\nRASEZ\nRASHS\nRASTA\nRATAI\nRATAS\nRATAT\nRATEE\nRATEL\nRATER\nRATES\nRATEZ\nRATIO\nRATON\nRAVES\nRAVIE\nRAVIN\nRAVIR\nRAVIS\nRAVIT\nRAYAI\nRAYAS\nRAYAT\nRAYEE\nRAYER\nRAYES\nRAYEZ\nRAYON\nREACS\nREAGI\nREAIS\nREAIT\nREALE\nREANT\nREAUX\nREBAB\nREBAT\nREBEC\nREBUS\nREBUT\nRECEL\nRECES\nRECEZ\nRECHE\nRECIF\nRECIT\nRECRU\nRECTA\nRECTO\nRECUE\nRECUL\nRECUS\nRECUT\nREDAN\nREDIE\nREDIS\nREDIT\nREDUE\nREDUS\nREDUT\nREELS\nREELU\nREENT\nREERA\nREFIS\nREFIT\nREFUS\nREGAL\nREGIE\nREGIR\nREGIS\nREGIT\nREGLA\nREGLE\nREGLO\nREGNA\nREGNE\nREIEZ\nREINE\nREINS\nREJET\nRELAX\nRELIA\nRELIE\nRELIS\nRELIT\nRELUE\nRELUS\nRELUT\nREMET\nREMIS\nREMIT\nREMIZ\nREMUA\nREMUE\nRENAL\nRENDE\nRENDS\nRENDU\nRENEE\nRENES\nRENIA\nRENIE\nRENNE\nRENOM\nRENTA\nRENTE\nREONS\nREPAS\nREPIC\nREPIT\nREPLI\nREPLU\nREPOS\nREPUE\nREPUS\nREPUT\nRESTA\nRESTE\nRETIF\nRETRO\nREUNI\nREVAI\nREVAS\nREVAT\nREVEE\nREVER\nREVES\nREVET\nREVEZ\nREVIS\nREVIT\nREVUE\nREVUS\nRHUMA\nRHUMB\nRHUME\nRHUMS\nRIAIS\nRIAIT\nRIALS\nRIANT\nRIBLA\nRIBLE\nRICHE\nRICIN\nRIDAI\nRIDAS\nRIDAT\nRIDEE\nRIDER\nRIDES\nRIDEZ\nRIELS\nRIENS\nRIENT\nRIEUR\nRIFLA\nRIFLE\nRIFTS\nRIIEZ\nRIMAI\nRIMAS\nRIMAT\nRIMEE\nRIMER\nRIMES\nRIMEZ\nRINCA\nRINCE\nRINGS\nRIONS\nRIPAI\nRIPAS\nRIPAT\nRIPEE\nRIPER\nRIPES\nRIPEZ\nRIRAI\nRIRAS\nRIRES\nRIREZ\nRISEE\nRISSE\nRITAL\nRITES\nRIVAI\nRIVAL\nRIVAS\nRIVAT\nRIVEE\nRIVER\nRIVES\nRIVET\nRIVEZ\nRIXES\nROBAI\nROBAS\nROBAT\nROBEE\nROBER\nROBES\nROBEZ\nROBIN\nROBOT\nROCHE\nROCKS\nROCOU\nRODAI\nRODAS\nRODAT\nRODEE\nRODEO\nRODER\nRODES\nRODEZ\nROGNA\nROGNE\nROGUE\nROIDE\nROIDI\nROLES\nROMAN\nROMPE\nROMPS\nROMPT\nROMPU\nRONCE\nRONDE\nRONDI\nRONDO\nRONDS\nRONEO\nRONGE\nROQUA\nROQUE\nROSAI\nROSAS\nROSAT\nROSEE\nROSER\nROSES\nROSEZ\nROSIE\nROSIR\nROSIS\nROSIT\nROSSA\nROSSE\nROTAI\nROTAS\nROTAT\nROTER\nROTES\nROTEZ\nROTIE\nROTIN\nROTIR\nROTIS\nROTIT\nROTOR\nROUAI\nROUAN\nROUAS\nROUAT\nROUEE\nROUER\nROUES\nROUET\nROUEZ\nROUFS\nROUGE\nROUGI\nROUIE\nROUIR\nROUIS\nROUIT\nROULA\nROULE\nROUMI\nROUND\nROUTA\nROUTE\nROYAL\nRUADE\nRUAIS\nRUAIT\nRUANT\nRUBAN\nRUBIS\nRUCHA\nRUCHE\nRUDES\nRUEES\nRUENT\nRUERA\nRUGBY\nRUGIR\nRUGIS\nRUGIT\nRUIEZ\nRUILA\nRUILE\nRUINA\nRUINE\nRUMBA\nRUMEN\nRUMEX\nRUOLZ\nRUONS\nRUPIN\nRURAL\nRUSAI\nRUSAS\nRUSAT\nRUSEE\nRUSER\nRUSES\nRUSEZ\nRUSHS\nRUSSE\nSABIR\nSABLA\nSABLE\nSABOT\nSABRA\nSABRE\nSACHE\nSACRA\nSACRE\nSAFRE\nSAGAS\nSAGES\nSAGOU\nSAGUM\nSAHEL\nSAIGA\nSAINE\nSAINS\nSAINT\nSAISI\nSAITE\nSAJOU\nSAKES\nSAKIS\nSALAI\nSALAS\nSALAT\nSALEE\nSALEP\nSALER\nSALES\nSALEZ\nSALIE\nSALIN\nSALIR\nSALIS\nSALIT\nSALLE\nSALOL\nSALON\nSALOP\nSALSA\nSALSE\nSALUA\nSALUE\nSALUT\nSALVE\nSAMBA\nSAMIT\nSAMPI\nSANAS\nSANGS\nSANIE\nSANTE\nSANVE\nSANZA\nSAOUL\nSAPAI\nSAPAS\nSAPAT\nSAPEE\nSAPER\nSAPES\nSAPEZ\nSAPIN\nSAQUA\nSAQUE\nSARDE\nSARIS\nSAROS\nSASSA\nSASSE\nSATIN\nSATIS\nSAUCA\nSAUCE\nSAUGE\nSAULE\nSAUNA\nSAUNE\nSAURA\nSAURE\nSAURI\nSAURS\nSAUTA\nSAUTE\nSAUTS\nSAUVA\nSAUVE\nSAVEZ\nSAVON\nSAXES\nSAXON\nSAXOS\nSAYON\nSBIRE\nSCALP\nSCARE\nSCATS\nSCEAU\nSCENE\nSCHAH\nSCIAI\nSCIAS\nSCIAT\nSCIEE\nSCIER\nSCIES\nSCIEZ\nSCION\nSCOOP\nSCORE\nSCOUT\nSCULL\nSCUTA\nSEANT\nSEAUX\nSEBKA\nSEBUM\nSECHA\nSECHE\nSECTE\nSEIDE\nSEIME\nSEINE\nSEING\nSEINS\nSEIZE\nSELFS\nSELLA\nSELLE\nSELON\nSELTZ\nSELVE\nSEMAI\nSEMAS\nSEMAT\nSEMEE\nSEMER\nSEMES\nSEMEZ\nSEMIS\nSENAT\nSENAU\nSENES\nSENNE\nSENSE\nSENTE\nSENTI\nSEOIR\nSEPIA\nSERAC\nSERAI\nSERAS\nSERBE\nSEREZ\nSERFS\nSERGE\nSERIA\nSERIE\nSERIN\nSERPE\nSERRA\nSERRE\nSERTE\nSERTI\nSERUM\nSERVE\nSERVI\nSETON\nSEUIL\nSEULE\nSEULS\nSEVES\nSEVIR\nSEVIS\nSEVIT\nSEVRA\nSEVRE\nSEXES\nSEXTE\nSEXTO\nSEXUE\nSHAHS\nSHAKO\nSHOOT\nSHORT\nSHOWS\nSHUNT\nSICLE\nSIEGE\nSIENS\nSIERA\nSIEUR\nSIGLE\nSIGMA\nSIGNA\nSIGNE\nSILEX\nSILOS\nSIMAS\nSINGE\nSINON\nSINUS\nSIOUX\nSIRES\nSIREX\nSIRLI\nSIROP\nSISAL\nSISES\nSITAR\nSITES\nSITOT\nSITUA\nSITUE\nSIUMS\nSIXTE\nSKAIS\nSKATE\nSKIAI\nSKIAS\nSKIAT\nSKIER\nSKIES\nSKIEZ\nSKIFF\nSKIFS\nSLANG\nSLAVE\nSLIPS\nSLOOP\nSLOWS\nSMALA\nSMALT\nSMART\nSMASH\nSMOGS\nSMOLT\nSMURF\nSNACK\nSNIFF\nSNOBA\nSNOBE\nSNOBS\nSOBRE\nSOCLE\nSODAS\nSODEE\nSODES\nSOEUR\nSOFAS\nSOIES\nSOIFS\nSOINS\nSOIRS\nSOJAS\nSOLDA\nSOLDE\nSOLEN\nSOLES\nSOLEX\nSOLIN\nSOLOS\nSOMAS\nSOMMA\nSOMME\nSONAR\nSONDA\nSONDE\nSONGE\nSONNA\nSONNE\nSONOS\nSORBE\nSORES\nSORTE\nSORTI\nSORTS\nSOSIE\nSOTCH\nSOTIE\nSOTTE\nSOUCI\nSOUDA\nSOUDE\nSOUES\nSOUKS\nSOULA\nSOULE\nSOULS\nSOUPA\nSOUPE\nSOURD\nSOURI\nSOUTE\nSOYAS\nSOYER\nSOYEZ\nSPAHI\nSPART\nSPATH\nSPEOS\nSPHEX\nSPIRE\nSPORE\nSPORT\nSPOTS\nSPRAT\nSPRAY\nSPRUE\nSQUAT\nSQUAW\nSTADE\nSTAFF\nSTAGE\nSTAND\nSTARS\nSTASE\nSTEAK\nSTELE\nSTEMM\nSTEMS\nSTENO\nSTERA\nSTERE\nSTICK\nSTIPE\nSTOCK\nSTOPS\nSTORE\nSTOUT\nSTRAS\nSTRIA\nSTRIE\nSTRIX\nSTUCS\nSTYLA\nSTYLE\nSTYLO\nSUAGE\nSUAIS\nSUAIT\nSUANT\nSUAVE\nSUBER\nSUBIE\nSUBIR\nSUBIS\nSUBIT\nSUCAI\nSUCAS\nSUCAT\nSUCEE\nSUCER\nSUCES\nSUCEZ\nSUCON\nSUCRA\nSUCRE\nSUEDE\nSUEES\nSUENT\nSUERA\nSUEUR\nSUIES\nSUIEZ\nSUIFA\nSUIFE\nSUIFS\nSUINT\nSUITE\nSUIVE\nSUIVI\nSUJET\nSULKY\nSUMAC\nSUMES\nSUNNA\nSUONS\nSUPER\nSUPIN\nSUPRA\nSURAH\nSURES\nSURET\nSURFA\nSURFE\nSURFS\nSURGI\nSURIN\nSURIR\nSURIS\nSURIT\nSUROS\nSUSHI\nSUSSE\nSUTES\nSUTRA\nSWAPS\nSWING\nSYLVE\nSYMPA\nTABAC\nTABAR\nTABES\nTABLA\nTABLE\nTABOR\nTABOU\nTACCA\nTACET\nTACHA\nTACHE\nTACON\nTACOT\nTACTS\nTAFIA\nTAGAL\nTAIES\nTAIGA\nTAINS\nTAIRA\nTAIRE\nTAISE\nTALAI\nTALAS\nTALAT\nTALCS\nTALEE\nTALER\nTALES\nTALEZ\nTALLA\nTALLE\nTALON\nTALUS\nTAMIA\nTAMIL\nTAMIS\nTANCA\nTANCE\nTANGO\nTANIN\nTANKS\nTANNA\nTANNE\nTANTE\nTAONS\nTAPAI\nTAPAS\nTAPAT\nTAPEE\nTAPER\nTAPES\nTAPEZ\nTAPIE\nTAPIN\nTAPIR\nTAPIS\nTAPIT\nTAPON\nTAQUA\nTAQUE\nTARAI\nTARAS\nTARAT\nTARDA\nTARDE\nTAREE\nTARER\nTARES\nTARET\nTAREZ\nTARGE\nTARIE\nTARIF\nTARIN\nTARIR\nTARIS\nTARIT\nTAROT\nTARSE\nTARTE\nTARTI\nTASSA\nTASSE\nTATAI\nTATAS\nTATAT\nTATEE\nTATER\nTATES\nTATEZ\nTATOU\nTAULE\nTAUPE\nTAURE\nTAXAI\nTAXAS\nTAXAT\nTAXEE\nTAXER\nTAXES\nTAXEZ\nTAXIE\nTAXIS\nTAXON\nTCHAO\nTECKS\nTEINS\nTEINT\nTELES\nTELEX\nTELLE\nTELLS\nTEMPE\nTEMPO\nTEMPS\nTENDE\nTENDS\nTENDU\nTENEZ\nTENIA\nTENIR\nTENON\nTENOR\nTENTA\nTENTE\nTENUE\nTENUS\nTERCA\nTERCE\nTERME\nTERNE\nTERNI\nTERRA\nTERRE\nTERRI\nTERSA\nTERSE\nTESTA\nTESTE\nTESTS\nTETAI\nTETAS\nTETAT\nTETEE\nTETER\nTETES\nTETEZ\nTETIN\nTETON\nTETRA\nTETTE\nTETUE\nTETUS\nTEXAN\nTEXTE\nTHAIE\nTHEME\nTHESE\nTHETA\nTHONS\nTHORA\nTHUNE\nTHUYA\nTHYMS\nTIANS\nTIARE\nTIBIA\nTIEDE\nTIEDI\nTIENS\nTIENT\nTIERS\nTIFFE\nTIGES\nTIGRA\nTIGRE\nTILDE\nTILLA\nTILLE\nTILTS\nTIMON\nTINTA\nTINTE\nTIQUA\nTIQUE\nTIRAI\nTIRAS\nTIRAT\nTIREE\nTIRER\nTIRES\nTIRET\nTIREZ\nTISAI\nTISAS\nTISAT\nTISEE\nTISER\nTISES\nTISEZ\nTISON\nTISSA\nTISSE\nTISSU\nTITAN\nTITIS\nTITRA\nTITRE\nTMESE\nTOAST\nTOGES\nTOILA\nTOILE\nTOISA\nTOISE\nTOITS\nTOKAI\nTOKAY\nTOLEE\nTOLES\nTOLET\nTOLLE\nTOLUS\nTOMAI\nTOMAS\nTOMAT\nTOMBA\nTOMBE\nTOMEE\nTOMER\nTOMES\nTOMEZ\nTOMME\nTOMMY\nTONAL\nTONDE\nTONDS\nTONDU\nTONIE\nTONKA\nTONNA\nTONNE\nTONTE\nTONUS\nTOPAI\nTOPAS\nTOPAT\nTOPER\nTOPES\nTOPEZ\nTOPOS\nTOQUA\nTOQUE\nTORAH\nTORDE\nTORDS\nTORDU\nTOREA\nTOREE\nTORII\nTORIL\nTORON\nTORSE\nTORTS\nTORVE\nTOTAL\nTOTEM\nTOTON\nTOTOS\nTOUAI\nTOUAS\nTOUAT\nTOUEE\nTOUER\nTOUES\nTOUEZ\nTOURD\nTOURS\nTOUTE\nTOUTS\nTRABE\nTRACA\nTRACE\nTRACS\nTRACT\nTRAHI\nTRAIE\nTRAIN\nTRAIS\nTRAIT\nTRAMA\nTRAME\nTRAMS\nTRAPU\nTREMA\nTREVE\nTRIAI\nTRIAL\nTRIAS\nTRIAT\nTRIBU\nTRIDI\nTRIEE\nTRIER\nTRIES\nTRIEZ\nTRIMA\nTRIME\nTRINE\nTRINS\nTRIOL\nTRIOS\nTRIPE\nTRIPS\nTROCS\nTROIS\nTROLL\nTRONA\nTRONC\nTRONE\nTROPE\nTROTS\nTROUA\nTROUE\nTROUS\nTRUCS\nTRUIE\nTRUST\nTSARS\nTUAGE\nTUAIS\nTUAIT\nTUANT\nTUBAI\nTUBAS\nTUBAT\nTUBEE\nTUBER\nTUBES\nTUBEZ\nTUEES\nTUENT\nTUERA\nTUEUR\nTUIEZ\nTUILA\nTUILE\nTULLE\nTUMES\nTUNER\nTUNES\nTUONS\nTUQUE\nTURBE\nTURCO\nTURCS\nTURFS\nTURNE\nTUSSE\nTUTES\nTUTIE\nTUTTI\nTUTUS\nTUYAU\nTWEED\nTWIST\nTYPAI\nTYPAS\nTYPAT\nTYPEE\nTYPER\nTYPES\nTYPEZ\nTYPHA\nTYPON\nTYPOS\nTYRAN\nTZARS\nUBACS\nUKASE\nULEMA\nULTRA\nULULA\nULULE\nULVES\nUNAUS\nUNIES\nUNION\nUNIRA\nUNITE\nURANE\nURATE\nUREES\nURGEA\nURGER\nURINA\nURINE\nURNES\nURUBU\nUSAGE\nUSAIS\nUSAIT\nUSANT\nUSEES\nUSENT\nUSERA\nUSIEZ\nUSINA\nUSINE\nUSITE\nUSNEE\nUSONS\nUSUEL\nUSURE\nUTILE\nUVALE\nUVAUX\nUVEES\nUVULA\nUVULE\nVACHE\nVAGIN\nVAGIR\nVAGIS\nVAGIT\nVAGUA\nVAGUE\nVAINC\nVAINE\nVAINS\nVAIRE\nVAIRS\nVALET\nVALEZ\nVALSA\nVALSE\nVALUE\nVALUS\nVALUT\nVALVE\nVAMPA\nVAMPE\nVAMPS\nVANDA\nVANNA\nVANNE\nVANTA\nVANTE\nVAPES\nVAQUA\nVAQUE\nVARAN\nVARIA\nVARIE\nVARUS\nVARVE\nVASES\nVASTE\nVEAUX\nVECES\nVECUE\nVECUS\nVECUT\nVEDAS\nVEINA\nVEINE\nVELAI\nVELAR\nVELAS\nVELAT\nVELDS\nVELER\nVELES\nVELEZ\nVELIE\nVELIN\nVELOS\nVELOT\nVELTE\nVELUE\nVELUM\nVELUS\nVENAL\nVENDE\nVENDS\nVENDU\nVENET\nVENEZ\nVENGE\nVENIN\nVENIR\nVENTA\nVENTE\nVENTS\nVENUE\nVENUS\nVERBE\nVERDI\nVERGE\nVERIN\nVERNI\nVERRA\nVERRE\nVERSA\nVERSE\nVERSO\nVERTE\nVERTS\nVERTU\nVERVE\nVESCE\nVESPA\nVESSA\nVESSE\nVESTE\nVETES\nVETEZ\nVETIR\nVETIS\nVETIT\nVETUE\nVETUS\nVEUFS\nVEULE\nVEUVE\nVEXAI\nVEXAS\nVEXAT\nVEXEE\nVEXER\nVEXES\nVEXEZ\nVIBRA\nVIBRE\nVICES\nVICHY\nVICIA\nVICIE\nVIDAI\nVIDAS\nVIDAT\nVIDEE\nVIDEO\nVIDER\nVIDES\nVIDEZ\nVIEIL\nVIENS\nVIENT\nVIEUX\nVIGIE\nVIGNE\nVILES\nVILLA\nVILLE\nVIMES\nVINAI\nVINAS\nVINAT\nVINEE\nVINER\nVINES\nVINEZ\nVINGT\nVIOCS\nVIOLA\nVIOLE\nVIOLS\nVIRAI\nVIRAL\nVIRAS\nVIRAT\nVIREE\nVIRER\nVIRES\nVIREZ\nVIRIL\nVIRUS\nVISAI\nVISAS\nVISAT\nVISEE\nVISER\nVISES\nVISEZ\nVISON\nVISSA\nVISSE\nVITAE\nVITAL\nVITES\nVITRA\nVITRE\nVIVAT\nVIVES\nVIVEZ\nVIVRA\nVIVRE\nVIZIR\nVOCAL\nVODKA\nVOEUX\nVOGUA\nVOGUE\nVOICI\nVOIES\nVOILA\nVOILE\nVOIRE\nVOLAI\nVOLAS\nVOLAT\nVOLEE\nVOLER\nVOLES\nVOLET\nVOLEZ\nVOLIS\nVOLTA\nVOLTE\nVOLTS\nVOLVE\nVOMER\nVOMIE\nVOMIR\nVOMIS\nVOMIT\nVOTAI\nVOTAS\nVOTAT\nVOTEE\nVOTER\nVOTES\nVOTEZ\nVOTIF\nVOTRE\nVOUAI\nVOUAS\nVOUAT\nVOUEE\nVOUER\nVOUES\nVOUEZ\nVOUGE\nVOULU\nVOUTA\nVOUTE\nVOYER\nVOYEZ\nVOYOU\nVRAIE\nVRAIS\nVULGO\nVULVE\nWAGON\nWATTS\nWEBER\nWHARF\nWHIGS\nWHIST\nWINCH\nXENON\nXERES\nXERUS\nXIPHO\nXYSTE\nYACHT\nYACKS\nYARDS\nYEBLE\nYEUSE\nYOGAS\nYOGIS\nYOLES\nYUCCA\nZABRE\nZAINS\nZANIS\nZANNI\nZANZI\nZAZOU\nZEBRA\nZEBRE\nZEBUS\nZELEE\nZELES\nZENDS\nZEROS\nZESTA\nZESTE\nZIBAI\nZIBAS\nZIBAT\nZIBEE\nZIBER\nZIBES\nZIBEZ\nZIGUA\nZIGUE\nZINCS\nZIPPA\nZIPPE\nZIZIS\nZLOTY\nZOILE\nZOMBI\nZONAI\nZONAL\nZONAS\nZONAT\nZONEE\nZONER\nZONES\nZONEZ\nZOOMS\nZOZOS"
  },
  {
    "path": "core/src/main/res/raw/wordle_list_pt.txt",
    "content": "ABACA\nABACO\nABADA\nABADE\nABALA\nABALO\nABANE\nABANO\nABATE\nABATI\nABAZA\nABCAZ\nABECE\nABECO\nABETO\nABOIE\nABOIO\nABOLA\nABONO\nABOTA\nABRAO\nABREU\nABRIA\nABRIL\nABRIR\nABRIS\nABRIU\nABUJA\nABUSO\nACABA\nACABE\nACABO\nACAIR\nACAJU\nACARA\nACARI\nACARO\nACASO\nACATA\nACAUA\nACCAO\nACENO\nACESA\nACESO\nACHAI\nACHAM\nACHAR\nACHAS\nACHEI\nACHEM\nACHOU\nACIDO\nACIMA\nACINO\nACOAM\nACOAR\nACOAS\nACODE\nACOEI\nACOEM\nACOES\nACTOR\nACUAI\nACUAM\nACUAR\nACUAS\nACUDE\nACUDI\nACUEI\nACUEM\nACUES\nACULE\nACULO\nACUOU\nACUSA\nACUSE\nACUSO\nACUTA\nADABA\nADAES\nADAGA\nADAMA\nADAME\nADAMO\nADEGA\nADEJE\nADEJO\nADEUS\nADIAI\nADIAM\nADIAR\nADIAS\nADIDA\nADIDO\nADIEI\nADIEM\nADIES\nADIOU\nADITE\nADITO\nADOBE\nADOBO\nADORA\nADORE\nADORO\nADOTE\nADOTO\nADRIA\nADRIO\nADUBO\nADUFE\nADULE\nADULO\nADVIR\nAECIA\nAECIO\nAERAR\nAEREA\nAEREO\nAFAGO\nAFALA\nAFAMA\nAFANO\nAFARE\nAFARO\nAFEAR\nAFETE\nAFETO\nAFIAR\nAFILO\nAFIXO\nAFLAR\nAFLUI\nAFOFE\nAFOFO\nAFOGO\nAFORA\nAFOXE\nAFUDE\nAFULO\nAFURA\nAGADE\nAGAPE\nAGATA\nAGATO\nAGAVE\nAGEUS\nAGINA\nAGINO\nAGITE\nAGITO\nAGNES\nAGOGO\nAGONO\nAGORA\nAGRAZ\nAGUAI\nAGUAR\nAGUAS\nAGUDA\nAGUDO\nAGUES\nAGUIA\nAIACE\nAIALA\nAINAO\nAINDA\nAIPIM\nAJAJA\nAJERU\nAJUDA\nAJUDE\nAJUDO\nAJUGA\nAJURU\nALABE\nALABO\nALADA\nALADI\nALADO\nALAES\nALAGA\nALAGE\nALALA\nALALC\nALANA\nALANE\nALANO\nALAOS\nALAVA\nALBAS\nALBOI\nALBOS\nALBUM\nALCAR\nALCAS\nALCEA\nALCEI\nALEIA\nALEMA\nALEMO\nALEPA\nALEPE\nALERJ\nALESC\nALESP\nALETO\nALFAR\nALFIM\nALGAR\nALGAS\nALGIA\nALGOL\nALGOR\nALGOZ\nALGUA\nALGUM\nALGUR\nALHAR\nALIAR\nALIAS\nALIBI\nALICE\nALIEN\nALIJO\nALINA\nALINE\nALINO\nALISE\nALMAS\nALMOS\nALOES\nALOJA\nALOJO\nALPES\nALTAI\nALTAR\nALTAS\nALTOS\nALUAR\nALUCO\nALUDE\nALUIR\nALUNE\nALUNO\nALVAR\nALVAS\nALVEO\nALVES\nALVIM\nALVOR\nALVOS\nAMADA\nAMADO\nAMAGO\nAMAIS\nAMAME\nAMAPA\nAMARA\nAMARO\nAMAVA\nAMBAR\nAMBAS\nAMBOS\nAMEAR\nAMEBA\nAMEIA\nAMEIS\nAMENO\nAMIAL\nAMIDO\nAMIGA\nAMIGO\nAMITA\nAMITO\nAMOJO\nAMORA\nAMPLO\nAMUAR\nANACO\nANAGE\nANAHY\nANAIS\nANAMA\nANANO\nANAOS\nANAPU\nANATA\nANCHO\nANCIA\nANDAI\nANDAM\nANDAR\nANDAS\nANDEI\nANDEM\nANDES\nANDOA\nANDOR\nANDOU\nANDRE\nANEIS\nANEJO\nANELO\nANEXE\nANEXO\nANGLO\nANHAR\nANIAO\nANIMA\nANIME\nANIMO\nANINA\nANINE\nANINO\nANION\nANITA\nANITO\nANIXO\nANJOS\nANODO\nANOES\nANOIA\nANONA\nANORI\nANOTE\nANOTO\nANSIA\nANTAO\nANTAR\nANTAS\nANTES\nANTEU\nANTRO\nANUAL\nANUIR\nANUIU\nANULE\nANULO\nANURO\nANZOL\nAONDE\nAONIA\nAORTA\nAPAGE\nAPARA\nAPARE\nAPARO\nAPEAR\nAPEGO\nAPELA\nAPELO\nAPIAI\nAPICE\nAPITO\nAPODE\nAPODI\nAPODO\nAPOIO\nAPOJO\nAPOLO\nAPORA\nAPORE\nAPPLE\nAPUPO\nAPURE\nAPURO\nAQUEM\nARABE\nARACA\nARACI\nARACU\nARADO\nARAMA\nARAME\nARAMO\nARANA\nARARA\nARARI\nARAUA\nARAXA\nARCAO\nARCAR\nARCAZ\nARCHA\nARCOL\nARCOS\nARDER\nARDIA\nARDIL\nARDIS\nARDOR\nARDUO\nAREAL\nAREAR\nAREAS\nARECA\nAREIA\nAREJE\nAREJO\nARELA\nARELE\nARELO\nARENA\nAREPA\nARERE\nAREUS\nARFAR\nARGAN\nARGEL\nARGON\nARGOS\nARIAL\nARIAS\nARIDO\nARIEL\nARIES\nARIGO\nARILO\nARIMO\nARIOS\nARJAO\nARMAI\nARMAM\nARMAR\nARMAS\nARMEI\nARMEM\nARMES\nARMOU\nARMUR\nARNAL\nARNAZ\nARNES\nAROLA\nAROLO\nAROMA\nAROMO\nAROSI\nARPAO\nARPAR\nARPEU\nARRAS\nARRAU\nARREU\nARROZ\nARTES\nARTUR\nARUBA\nARUJA\nARUJO\nARULA\nARUME\nARXAR\nASAIS\nASARA\nASAVA\nASCII\nASCUA\nASEIS\nASILO\nASNAL\nASNAR\nASNOS\nASPAR\nASPAS\nASSAI\nASSAM\nASSAR\nASSAS\nASSAZ\nASSEI\nASSEM\nASSES\nASSIM\nASSIS\nASSOU\nASSUA\nASTRO\nASTUR\nATADO\nATAIS\nATARA\nATAVA\nATEAR\nATEIA\nATEIS\nATENA\nATESE\nATESO\nATEUS\nATICA\nATICO\nATILA\nATINO\nATIRE\nATIRO\nATIVA\nATIVE\nATIVO\nATLAS\nATOMO\nATONI\nATONO\nATRAS\nATREU\nATRIL\nATRIO\nATRIZ\nATROA\nATROO\nATROS\nATROZ\nATUAI\nATUAL\nATUAM\nATUAR\nATUAS\nATUEI\nATUEM\nATUES\nATUOU\nATXIM\nAUCAO\nAUDAZ\nAUDIO\nAUETE\nAUETI\nAUREA\nAUREO\nAURIR\nAUTOR\nAUTOS\nAVAIS\nAVARA\nAVARE\nAVARO\nAVATI\nAVEIA\nAVELA\nAVENA\nAVIAO\nAVIAR\nAVIDO\nAVISA\nAVISE\nAVISO\nAWETI\nAXILA\nAXIXA\nAZADO\nAZARE\nAZEDO\nAZERI\nAZIMO\nAZOAI\nAZOAM\nAZOAR\nAZOAS\nAZOEI\nAZOEM\nAZOES\nAZOOU\nAZOTO\nAZUIS\nAZULE\nAZULO\nBABAI\nBABAM\nBABAO\nBABAS\nBABAU\nBABEI\nBABEL\nBABEM\nBABES\nBABOU\nBACAR\nBACIA\nBACIO\nBACON\nBADIO\nBAETA\nBAFAR\nBAFIO\nBAGOA\nBAGRE\nBAGUA\nBAHIA\nBAIAO\nBAIAR\nBAILA\nBAILE\nBAILO\nBAITA\nBAITE\nBAIXA\nBAIXE\nBAIXO\nBALAO\nBALAR\nBALAS\nBALBO\nBALDA\nBALDE\nBALES\nBALGA\nBALHO\nBALIR\nBALIU\nBALOR\nBALSA\nBAMBA\nBAMBE\nBAMBI\nBAMBO\nBAMBU\nBANAL\nBANCA\nBANCO\nBANDA\nBANDE\nBANDO\nBANGA\nBANGO\nBANHA\nBANHO\nBANIR\nBANJO\nBANTO\nBANTU\nBANZA\nBANZE\nBANZO\nBAOBA\nBARAO\nBARBA\nBARBO\nBARCA\nBARCO\nBARDA\nBARDO\nBAREM\nBARGA\nBARIO\nBARRA\nBARRE\nBARRO\nBASAA\nBASAL\nBASCA\nBASCO\nBASES\nBASSE\nBASTA\nBASTE\nBASTO\nBATCH\nBATEL\nBATER\nBATOM\nBATON\nBAURU\nBAZAR\nBEATA\nBEATO\nBEBAS\nBEBEM\nBEBER\nBEBEU\nBEBUM\nBECAS\nBECHA\nBECHE\nBEDEL\nBEICO\nBEIGE\nBEIJA\nBEIJE\nBEIJO\nBEIJU\nBEIRA\nBEIRO\nBEITA\nBELAS\nBELEM\nBELGA\nBELOS\nBEMBA\nBEMOL\nBENGA\nBENIM\nBENIN\nBENTO\nBEQUE\nBERCA\nBERCE\nBERCO\nBERMA\nBERNA\nBERRO\nBESTA\nBESTE\nBESTO\nBETAO\nBETAR\nBETIM\nBHILI\nBIANA\nBIARI\nBICAL\nBICAR\nBICAS\nBICHA\nBICHO\nBICLA\nBICOS\nBIDUO\nBIELA\nBIFAR\nBIGLE\nBIGUE\nBIJOU\nBILAC\nBILAR\nBILES\nBILHA\nBILIS\nBILRO\nBIMBA\nBIMBE\nBIMBO\nBINAR\nBINGA\nBINGO\nBIOCO\nBIOMA\nBIOTA\nBIPES\nBIRAO\nBIRRA\nBISAI\nBISAM\nBISAO\nBISAR\nBISAS\nBISCA\nBISEI\nBISEL\nBISEM\nBISES\nBISOU\nBISPA\nBISPO\nBITAR\nBIVIO\nBLASE\nBLEFE\nBLINI\nBLITZ\nBLOCO\nBLOGA\nBLUCO\nBLUES\nBLUFF\nBLUSA\nBNDES\nBOATE\nBOATO\nBOAVA\nBOBAR\nBOBOT\nBOCAL\nBOCAO\nBOCAS\nBOCHA\nBOCHE\nBOCOI\nBODAO\nBODAS\nBOFAS\nBOIAO\nBOIAR\nBOICA\nBOIEI\nBOIIL\nBOINA\nBOIRA\nBOITA\nBOITE\nBOJAR\nBOLAR\nBOLAS\nBOLBO\nBOLHA\nBOLOR\nBOLSA\nBOLSO\nBOMBA\nBOMBO\nBONDE\nBONGO\nBONUS\nBONZO\nBORAS\nBORAX\nBORBA\nBORDA\nBORDE\nBORDO\nBORIA\nBORLA\nBORNE\nBOROA\nBORRA\nBORRO\nBOSAO\nBOSON\nBOSSA\nBOSTA\nBOSTE\nBOSTO\nBOTAI\nBOTAM\nBOTAO\nBOTAR\nBOTAS\nBOTEI\nBOTEM\nBOTES\nBOTIM\nBOTON\nBOTOU\nBOTOX\nBOUBA\nBOUCA\nBOXER\nBOXES\nBRABO\nBRACA\nBRACO\nBRADA\nBRADO\nBRAGA\nBRAMA\nBRASA\nBRAVA\nBRAVO\nBRAVU\nBRECA\nBREGA\nBREIA\nBREJA\nBREJO\nBRENA\nBRENO\nBRETE\nBREVE\nBRIAL\nBRICA\nBRIDA\nBRIGA\nBRIGO\nBRIOL\nBRISA\nBRIZA\nBROAR\nBROAS\nBROCA\nBROCO\nBRODO\nBROMO\nBROTA\nBROTE\nBROTO\nBROXA\nBROXE\nBROXO\nBRUAR\nBRUMA\nBRUNA\nBRUNE\nBRUNI\nBRUNO\nBRUTA\nBRUTO\nBRUXA\nBRUXO\nBUCAL\nBUCHA\nBUCHO\nBUCIL\nBUCLE\nBUCOS\nBUENO\nBUFAO\nBUFAR\nBUGAR\nBUGIA\nBUGIE\nBUGIO\nBUGRE\nBUGUE\nBULAR\nBULAS\nBULBO\nBULHA\nBULHO\nBULIR\nBUMBO\nBUNDA\nBUNHO\nBUQUE\nBURCA\nBUREL\nBURGO\nBURIL\nBURLA\nBURRA\nBURRO\nBUSAO\nBUSCA\nBUSTO\nBUTAO\nBUTEO\nBUTES\nBUTIA\nBUTIM\nBUTIO\nBUTUA\nBUXAO\nBUZIO\nCAABA\nCABAL\nCABAU\nCABAZ\nCABEM\nCABER\nCABIS\nCABOS\nCABOZ\nCABRA\nCABRO\nCABUM\nCACAO\nCACAR\nCACAU\nCACHA\nCACHE\nCACHO\nCACRE\nCACTO\nCACUA\nCADEA\nCAETE\nCAFES\nCAFRA\nCAFRE\nCAFUA\nCAGAO\nCAGAR\nCAIAR\nCAIBI\nCAIBO\nCAICO\nCAIDA\nCAIDO\nCAINS\nCAIOS\nCAIRA\nCAIRO\nCAIRU\nCAIUA\nCAIXA\nCALAI\nCALAM\nCALAO\nCALAR\nCALAS\nCALCA\nCALCE\nCALCO\nCALDA\nCALDO\nCALEI\nCALEM\nCALES\nCALFE\nCALHA\nCALHE\nCALIX\nCALIZ\nCALMA\nCALMO\nCALOM\nCALOR\nCALOS\nCALOU\nCALVA\nCALVE\nCALVO\nCAMAL\nCAMAS\nCAMBA\nCAMBE\nCAMBO\nCAMIM\nCAMPA\nCAMPO\nCANAA\nCANAL\nCANAS\nCANCA\nCANDA\nCANDO\nCANGA\nCANGO\nCANHA\nCANIL\nCANJA\nCANLE\nCANOA\nCANON\nCANOS\nCANSE\nCANSO\nCANTA\nCANTE\nCANTO\nCANZA\nCAPAI\nCAPAM\nCAPAO\nCAPAR\nCAPAS\nCAPAZ\nCAPEM\nCAPES\nCAPIM\nCAPOA\nCAPOT\nCAPOU\nCAPUT\nCAPUZ\nCAQUI\nCARAA\nCARAI\nCARAS\nCARAY\nCARDA\nCARDO\nCARGA\nCARGO\nCARIE\nCARIL\nCARIZ\nCARLA\nCARLO\nCARMA\nCARME\nCARMO\nCARNE\nCAROS\nCARPA\nCARPE\nCARPO\nCARRO\nCARTA\nCARTE\nCASAI\nCASAL\nCASAM\nCASAO\nCASAR\nCASAS\nCASCA\nCASCO\nCASEI\nCASEM\nCASES\nCASOS\nCASOU\nCASPA\nCASSA\nCASSE\nCASSO\nCASTA\nCASTE\nCASTO\nCATAI\nCATAM\nCATAR\nCATAS\nCATEI\nCATEM\nCATES\nCATOU\nCATRE\nCATUA\nCAUAS\nCAUDA\nCAUES\nCAUIM\nCAULE\nCAUPI\nCAURI\nCAUSA\nCAUSO\nCAUTA\nCAUTO\nCAVAI\nCAVAM\nCAVAR\nCAVAS\nCAVEA\nCAVEI\nCAVEM\nCAVES\nCAVOU\nCEARA\nCEDEM\nCEDER\nCEDRO\nCEGAR\nCEIBA\nCEIFA\nCEIFE\nCEIFO\nCEITA\nCEIVE\nCELGA\nCELHA\nCELIA\nCELIO\nCELME\nCELSA\nCELSO\nCELTA\nCEMBA\nCENAS\nCENHO\nCENSO\nCENTO\nCERCA\nCERCE\nCERCO\nCERDA\nCERDO\nCERES\nCERIO\nCERNE\nCEROL\nCERRA\nCERRE\nCERRO\nCERTA\nCERTO\nCERVA\nCERVO\nCESAR\nCESIO\nCESSA\nCESSE\nCESSO\nCESTA\nCESTO\nCETIM\nCETRO\nCEUTA\nCEVAR\nCHABU\nCHACO\nCHADA\nCHADE\nCHAGA\nCHALE\nCHAMA\nCHANA\nCHAPA\nCHAPE\nCHAPO\nCHARA\nCHATA\nCHATO\nCHAUL\nCHAVE\nCHAVO\nCHECO\nCHEFA\nCHEFE\nCHEGA\nCHEIA\nCHEIO\nCHEKA\nCHEVA\nCHIAO\nCHIAR\nCHIBA\nCHIBE\nCHIBO\nCHICA\nCHICO\nCHILE\nCHINA\nCHIPE\nCHITA\nCHITO\nCHOCA\nCHOCO\nCHOER\nCHOLA\nCHOLO\nCHONA\nCHOPE\nCHORA\nCHORE\nCHORO\nCHOTO\nCHUCA\nCHUCO\nCHUFA\nCHULA\nCHULE\nCHULO\nCHUTA\nCHUTE\nCHUTO\nCHUVA\nCIADA\nCIANO\nCIAOS\nCIBAR\nCICLO\nCIDRA\nCIFRA\nCIFRE\nCIFRO\nCIGAS\nCILHA\nCILHO\nCILIO\nCIMAO\nCINCO\nCINTA\nCINTO\nCINZA\nCIOSO\nCIPRA\nCIPRO\nCIRCO\nCIRIA\nCIRIO\nCIRRO\nCISAO\nCISCA\nCISCO\nCISMA\nCISNE\nCISTO\nCITAI\nCITAM\nCITAR\nCITAS\nCITEI\nCITEM\nCITES\nCITOU\nCITRO\nCIUME\nCIVIL\nCLADO\nCLAIM\nCLAME\nCLAMO\nCLARA\nCLARO\nCLAVA\nCLAVE\nCLEAN\nCLERO\nCLICA\nCLICK\nCLIMA\nCLINA\nCLIPE\nCLONE\nCLORO\nCLOSE\nCLOTO\nCLOWN\nCLUBE\nCOAIS\nCOALA\nCOARA\nCOARI\nCOAVA\nCOBOI\nCOBRA\nCOBRE\nCOBRI\nCOBRO\nCOCAL\nCOCAO\nCOCAR\nCOCHA\nCOCHE\nCOCHO\nCOCOS\nCODEA\nCODEO\nCODEX\nCOEIS\nCOESO\nCOEVO\nCOFRE\nCOIAS\nCOICE\nCOIFA\nCOIMA\nCOIME\nCOINA\nCOINE\nCOIRA\nCOIRO\nCOISA\nCOISO\nCOITA\nCOITE\nCOITO\nCOLAI\nCOLAM\nCOLAR\nCOLAS\nCOLEI\nCOLEM\nCOLES\nCOLMA\nCOLMO\nCOLON\nCOLOU\nCOLZA\nCOMAM\nCOMAS\nCOMBE\nCOMBO\nCOMER\nCOMES\nCOMEU\nCOMIA\nCOMUA\nCOMUM\nCONAR\nCONAS\nCONCA\nCONDE\nCONGO\nCONHA\nCONHO\nCONTA\nCONTE\nCONTO\nCOPAM\nCOPAO\nCOPAR\nCOPAS\nCOPIA\nCOPIE\nCOPIO\nCOPLA\nCOPOM\nCOPRA\nCOPTA\nCOQUE\nCORAI\nCORAL\nCORAM\nCORAO\nCORAR\nCORAS\nCORCA\nCORCO\nCORDA\nCOREI\nCOREM\nCORES\nCORGA\nCORGO\nCORJA\nCORNE\nCORNO\nCOROA\nCOROS\nCOROU\nCORPO\nCORRA\nCORRE\nCORRI\nCORRO\nCORSO\nCORTA\nCORTE\nCORTO\nCORVA\nCORVO\nCOSCA\nCOSCO\nCOSER\nCOSEU\nCOSMA\nCOSME\nCOSMO\nCOSTA\nCOSTO\nCOTAO\nCOTAR\nCOTAS\nCOTIA\nCOTRA\nCOUBE\nCOUCE\nCOUPE\nCOURA\nCOURO\nCOUSA\nCOUTO\nCOUVE\nCOVAS\nCOVIL\nCOXIA\nCOXIM\nCOZER\nCOZEU\nCRACK\nCRASE\nCRASH\nCRATO\nCRAVE\nCRAVO\nCRAWL\nCRECE\nCREDE\nCREDO\nCREEM\nCREGA\nCREGO\nCREIA\nCREIO\nCREME\nCREPE\nCRESO\nCRIAI\nCRIAM\nCRIAR\nCRIAS\nCRICA\nCRIEI\nCRIEM\nCRIES\nCRIME\nCRINA\nCRIOU\nCRISE\nCRIVO\nCROAC\nCROCA\nCROIA\nCROIO\nCROMO\nCRUDE\nCRUEL\nCRUSH\nCUAIS\nCUATA\nCUBAR\nCUCAR\nCUCHO\nCUDAR\nCUECA\nCUICA\nCUIDA\nCUIDO\nCUITE\nCUJAS\nCUJOS\nCULPA\nCULPE\nCULPO\nCULTO\nCUMBE\nCUMEL\nCUMIM\nCUMIO\nCUNCA\nCUNCO\nCUNHA\nCUNHO\nCUPIM\nCURAI\nCURAL\nCURAM\nCURAR\nCURAS\nCURDO\nCUREI\nCUREM\nCURES\nCURIA\nCURIO\nCUROU\nCURRA\nCURRO\nCURRY\nCURSO\nCURTA\nCURTO\nCURUA\nCURVA\nCURVE\nCURVO\nCUSCA\nCUSCO\nCUSPO\nCUSTA\nCUSTE\nCUSTO\nCUTER\nCUTIA\nCUTIS\nCUXIU\nCUZAO\nDACAO\nDACAR\nDACHA\nDADAS\nDADOR\nDADOS\nDAIME\nDAKAR\nDALEM\nDALIA\nDALVA\nDALVO\nDAMAO\nDAMAS\nDAMBA\nDAMNO\nDANAI\nDANAM\nDANAR\nDANCA\nDANCE\nDANDI\nDANDO\nDANEI\nDANEM\nDANES\nDANOS\nDANOU\nDAQUI\nDARAO\nDARCI\nDARDO\nDAREI\nDARIA\nDARIO\nDARMA\nDATAR\nDATAS\nDAUDE\nDAVAM\nDAVID\nDAVIS\nDEBIL\nDEBUT\nDECAM\nDECAS\nDECEI\nDECEM\nDECER\nDECES\nDECEU\nDECIA\nDEDAL\nDEDAO\nDEDOS\nDEISE\nDEITA\nDEITE\nDEITO\nDEIXA\nDEIXE\nDEIXO\nDELAS\nDELEI\nDELES\nDELIR\nDELTA\nDEMOS\nDENDE\nDENSA\nDENSO\nDENTE\nDEPOR\nDEPRE\nDEQUE\nDERAM\nDERBI\nDERBY\nDERMA\nDERME\nDESAR\nDESCA\nDESCE\nDESCI\nDESCO\nDESDE\nDESDO\nDESPI\nDESSA\nDESSE\nDESTA\nDESTE\nDETEM\nDETER\nDETOX\nDEUSA\nDEVER\nDEVES\nDEVIA\nDEVIR\nDIABA\nDIABO\nDIADE\nDIAFA\nDIANA\nDICAR\nDIECO\nDIEGA\nDIEGO\nDIESE\nDIETA\nDIGNA\nDIGNO\nDILDO\nDILMA\nDILMO\nDIMER\nDINAS\nDINDA\nDINGO\nDINKA\nDIODO\nDIOGA\nDIOGO\nDIONE\nDIQUE\nDIREI\nDISCO\nDISEL\nDISSE\nDISSO\nDISTO\nDITAR\nDITAS\nDITOS\nDIVAS\nDIVOS\nDIZEM\nDIZER\nDIZIA\nDOADA\nDOADO\nDOAIS\nDOARA\nDOAVA\nDOBRA\nDOBRE\nDOBRO\nDOCAR\nDOCEM\nDOCES\nDOCIL\nDODOI\nDODOS\nDOEIS\nDOERA\nDOGAO\nDOGMA\nDOGUE\nDOIAM\nDOIAS\nDOIDA\nDOIDO\nDOIRA\nDOIRO\nDOLAR\nDOLOR\nDOMAR\nDONAS\nDONDE\nDONDO\nDONGO\nDONOS\nDOPAR\nDORIS\nDORNA\nDORSO\nDORZE\nDOTAR\nDOUDA\nDOUDO\nDOULA\nDOURA\nDOURE\nDOURO\nDOUTO\nDRAFT\nDRAGA\nDRAMA\nDRENE\nDRENO\nDRIVE\nDROGA\nDROGO\nDRONE\nDROPE\nDROPS\nDRUPA\nDRUSA\nDRUSO\nDUBIA\nDUBIO\nDUBLE\nDUCAL\nDUCHA\nDUCHE\nDUCHO\nDUCTO\nDUELO\nDUERE\nDUETO\nDUMAS\nDUMBA\nDUPLA\nDUPLO\nDUQUE\nDURAI\nDURAM\nDURAR\nDURAS\nDUREI\nDUREM\nDURES\nDUROS\nDUROU\nDUTRA\nDUZIA\nEBANO\nEBOLA\nEBRIA\nEBRIO\nECLER\nECOAI\nECOAR\nECRAN\nEDEIA\nEDEMA\nEDENS\nEDITE\nEDITO\nEDNAS\nEDNOS\nEFEBO\nEFIRE\nEFODE\nEGIDE\nEGITO\nEIRAS\nEIVAR\nEIXOS\nELEMI\nELEPE\nELFOS\nELIAS\nELISA\nELISE\nELISO\nELITE\nELOAS\nELVAS\nELZAS\nELZOS\nEMAIL\nEMANE\nEMANO\nEMBUA\nEMERO\nEMESE\nEMICO\nEMOJI\nEMPAR\nEMPOS\nENCHE\nENCHO\nENDES\nENDEZ\nENDRO\nENFIA\nENFIM\nENGOS\nENGUA\nENJOO\nENOJA\nENOJO\nENTAO\nENTRA\nENTRE\nENTRO\nENVIO\nENZAS\nENZOS\nEPICA\nEPICO\nEPOCA\nEPOXI\nERADO\nERAIS\nERBIO\nEREIS\nERERE\nERETO\nERGUE\nERICA\nERICE\nERMAS\nERRAI\nERRAM\nERRAR\nERRAS\nERREI\nERREM\nERRES\nERROR\nERROU\nERVAS\nERZYA\nESCOL\nESFIA\nESGAR\nESMAR\nESPIA\nESPIE\nESPIO\nESPIR\nESPRU\nESQUI\nESSAS\nESSES\nESTAO\nESTAR\nESTAS\nESTER\nESTES\nESTIE\nESTIO\nESTOL\nESTOU\nESTRO\nETANO\nETAPA\nETENO\nETHOS\nETICA\nETICO\nETIMO\nETNIA\nETUDE\nEVADE\nEVITE\nEVITO\nEVORA\nEXALO\nEXAME\nEXATA\nEXATO\nEXIDO\nEXIME\nEXITO\nEXODO\nEXPOR\nEXTRA\nEXUMA\nEXUME\nEXUMO\nFABIA\nFABIO\nFACAM\nFACAO\nFACES\nFACHA\nFACHO\nFACIL\nFACTO\nFADOS\nFAIAL\nFAIAR\nFAINA\nFAITO\nFAIXA\nFALAI\nFALAM\nFALAR\nFALAS\nFALAZ\nFALCA\nFALDA\nFALEI\nFALEM\nFALES\nFALHA\nFALHE\nFALHO\nFALIR\nFALOU\nFALSA\nFALSO\nFALTA\nFALTE\nFALTO\nFALUS\nFANAL\nFANAR\nFANGA\nFANHA\nFANHO\nFANTA\nFARAD\nFARAO\nFARDA\nFARDO\nFARIA\nFAROL\nFARPA\nFARRA\nFARSA\nFARTA\nFARTE\nFARTO\nFARUM\nFASOR\nFASTA\nFASTO\nFATAL\nFATAO\nFATIA\nFATIE\nFATIO\nFATOR\nFATUO\nFAUCE\nFAULA\nFAUNA\nFAUNO\nFAVOR\nFAZEI\nFAZEM\nFAZER\nFAZIA\nFEBEU\nFEBRA\nFEBRE\nFECAL\nFECHA\nFECHE\nFECHO\nFEDER\nFEDOR\nFEIAS\nFEIJO\nFEIOS\nFEIRA\nFEIRE\nFEIRO\nFEITA\nFEITO\nFEIXE\nFELAS\nFELEO\nFELIX\nFELIZ\nFELPA\nFELPO\nFEMEA\nFEMEO\nFEMUR\nFENDA\nFENDE\nFENIX\nFENOL\nFENTO\nFEOFO\nFERAS\nFERAZ\nFERIA\nFERIE\nFERIO\nFERIR\nFERMI\nFEROS\nFEROZ\nFERRA\nFERRO\nFERVE\nFERVO\nFESTA\nFETAL\nFEUDO\nFEZES\nFIADO\nFIAMA\nFIAPO\nFIBRA\nFICAR\nFICHA\nFICOU\nFIGLE\nFIGOS\nFILAO\nFILER\nFILET\nFILHA\nFILHO\nFILIE\nFILIO\nFILME\nFILMO\nFIMIA\nFINAL\nFINAS\nFINCA\nFINDA\nFINDE\nFINDO\nFINES\nFINTA\nFIOFO\nFIOTA\nFIOTE\nFIQUE\nFIRMA\nFIRME\nFIRMO\nFISCO\nFISGA\nFITAR\nFITAS\nFIXAI\nFIXAM\nFIXAR\nFIXAS\nFIXEI\nFIXEM\nFIXES\nFIXOU\nFLAMA\nFLAPE\nFLASH\nFLATE\nFLATO\nFLAVA\nFLAVO\nFLEME\nFLETE\nFLIRT\nFLOCO\nFLORA\nFLORE\nFLORO\nFLUIR\nFLUOR\nFLUSH\nFLUXO\nFOBIA\nFOCAO\nFOCAR\nFODAO\nFODAS\nFODAZ\nFODER\nFOFAI\nFOFAM\nFOFAO\nFOFAR\nFOFAS\nFOFEI\nFOFEM\nFOFES\nFOFIS\nFOFOS\nFOFOU\nFOGAO\nFOGEM\nFOGES\nFOICE\nFOILA\nFOITO\nFOLAR\nFOLGA\nFOLHA\nFOLHE\nFOLHO\nFOLIA\nFOLIE\nFOLIO\nFOMES\nFOMOS\nFONTE\nFORAL\nFORAM\nFORAS\nFORCA\nFORCE\nFOREM\nFORES\nFORJA\nFORJO\nFORMA\nFORME\nFORNO\nFOROS\nFORRA\nFORRE\nFORRO\nFORTE\nFORUM\nFOSCA\nFOSCO\nFOSSA\nFOSSE\nFOSSO\nFOSTE\nFOTAO\nFOTON\nFOUCE\nFOULA\nFOUTO\nFRACA\nFRACO\nFRADE\nFRAGA\nFRAME\nFRAPE\nFRASE\nFREAR\nFREIO\nFRESA\nFRESO\nFRETE\nFRIAS\nFRILA\nFRIOS\nFRISA\nFRISO\nFRITA\nFRITO\nFRIUL\nFROCO\nFRONT\nFROTA\nFRUIR\nFRUIS\nFRUTA\nFRUTO\nFUCAR\nFUDER\nFUFIO\nFUGAR\nFUGAZ\nFUGIR\nFUGIU\nFULAR\nFULVA\nFULVO\nFUMAI\nFUMAM\nFUMAR\nFUMAS\nFUMEI\nFUMEM\nFUMES\nFUMOS\nFUMOU\nFUNAI\nFUNAR\nFUNDA\nFUNDO\nFUNGA\nFUNGO\nFUNIL\nFUNIS\nFUNJE\nFURAI\nFURAM\nFURAO\nFURAR\nFURAS\nFURCA\nFURCO\nFUREI\nFUREM\nFURES\nFURIA\nFURNA\nFUROR\nFUROU\nFURTE\nFURTO\nFUSAO\nFUSCA\nFUSCO\nFUSIL\nFUSTA\nFUSTE\nFUTIL\nFUTON\nFUTRE\nFUTUM\nFUZIL\nFUZUE\nGABAO\nGABAR\nGABIA\nGACHA\nGACHO\nGAFFE\nGAGUE\nGAIAR\nGAIAS\nGAIOS\nGAITA\nGAIVA\nGAIZO\nGAJAO\nGALAI\nGALAM\nGALAO\nGALAR\nGALAS\nGALEI\nGALEM\nGALES\nGALGA\nGALGO\nGALHA\nGALHO\nGALIA\nGALIO\nGALLO\nGALOU\nGAMAO\nGAMAR\nGAMAS\nGAMBA\nGAMBE\nGAMER\nGANDA\nGANDU\nGANES\nGANGA\nGANHA\nGANHE\nGANHO\nGANIR\nGANSO\nGARBO\nGARCA\nGARFO\nGAROA\nGARRA\nGARUA\nGASES\nGASTE\nGASTO\nGATAL\nGATAS\nGATIL\nGATOS\nGAVEA\nGAZUA\nGEADA\nGEBAR\nGEENA\nGEISE\nGELAI\nGELAM\nGELAR\nGELEI\nGELEM\nGELES\nGELHA\nGELOU\nGEMAR\nGEMAS\nGEMEO\nGEMER\nGENES\nGENIA\nGENIO\nGENRO\nGENTE\nGEODO\nGERAI\nGERAL\nGERAM\nGERAR\nGERAS\nGEREI\nGEREM\nGERES\nGERIA\nGERIR\nGERIS\nGERME\nGEROU\nGESSA\nGESSO\nGESTA\nGESTO\nGETAS\nGIBAO\nGICLE\nGICLO\nGINGA\nGINJA\nGINOS\nGIRAR\nGIRES\nGIRIA\nGIRIO\nGIROS\nGIRUA\nGLACE\nGLEBA\nGLENA\nGLIDE\nGLIFO\nGLOBO\nGLOSA\nGLOSE\nGLOSO\nGLOTE\nGLUON\nGNOMA\nGNOMO\nGNOSE\nGOELA\nGOFRE\nGOIAS\nGOIVA\nGOLAO\nGOLEM\nGOLES\nGOLFA\nGOLFE\nGOLFO\nGOLPE\nGOMAR\nGONGO\nGONIS\nGONZO\nGORAI\nGORAM\nGORAR\nGORAS\nGORDA\nGORDO\nGOREI\nGOREM\nGORES\nGORGA\nGORJA\nGOROU\nGORRO\nGOSMA\nGOSTA\nGOSTO\nGOTAS\nGOULI\nGOZAI\nGOZAM\nGOZAR\nGOZAS\nGOZEI\nGOZEM\nGOZES\nGOZOU\nGRAAL\nGRACA\nGRACO\nGRADE\nGRADO\nGRAFO\nGRAMA\nGRANA\nGRATA\nGRATO\nGRAUS\nGRAVA\nGRAVE\nGRAVO\nGRAXA\nGRAXO\nGREGA\nGREGO\nGREIS\nGREJO\nGRELO\nGRENA\nGRETA\nGREVE\nGRIDE\nGRIFE\nGRIFO\nGRILA\nGRILO\nGRIMA\nGRIOT\nGRIPE\nGRITA\nGRITE\nGRITO\nGROLO\nGROSA\nGROTA\nGRUDE\nGRUDO\nGRUPO\nGRUTA\nGUAJA\nGUAPE\nGUAPO\nGUARA\nGUDAO\nGUEBO\nGUEIS\nGUEJA\nGUEOS\nGUETO\nGUGLE\nGUGLO\nGUIAR\nGUICO\nGUIDO\nGUIGA\nGUINE\nGUIOU\nGUISA\nGUITA\nGUITO\nGUIZO\nGULAG\nGULIM\nGUNGA\nGURIA\nGURME\nGUSLA\nGUSPE\nHABIL\nHADES\nHADJI\nHAIKU\nHAITI\nHAJAM\nHAJAS\nHAKKA\nHALFE\nHALUX\nHANJA\nHANSA\nHAPAX\nHARAS\nHAREM\nHARPA\nHARTO\nHASHI\nHASTA\nHASTE\nHAUCA\nHAULE\nHAVAI\nHAVEI\nHAVER\nHAVIA\nHEDRA\nHELIO\nHEMOS\nHERDE\nHERDO\nHEREU\nHERMA\nHEROI\nHERTZ\nHIATO\nHIDRA\nHIDRO\nHIENA\nHIFEN\nHILAR\nHIMEN\nHINDI\nHINDU\nHIPER\nHIRTO\nHOBBY\nHOJES\nHOMEM\nHOMUM\nHONOR\nHONRA\nHORAR\nHORAS\nHORDA\nHORSA\nHORTA\nHORTO\nHORUS\nHOSCO\nHOSTE\nHOTEL\nHOUVE\nHULHA\nHUMOR\nHUMUS\nHURRA\nHUSAR\nHUSMA\nIACRI\nIAMOS\nIANSA\nIAQUE\nIARAS\nIBAMA\nIBATE\nIBEMA\nIBERO\nIBIAI\nIBIAM\nIBIRA\nIBOGA\nIBOPE\nICARA\nICARO\nICATU\nICONE\nIDADE\nIDAHO\nIDEAL\nIDEAR\nIDEIA\nIDOLA\nIDOLO\nIDOSA\nIDOSO\nIEMEM\nIEMEN\nIGACI\nIGARA\nIGNEO\nIGUAI\nIGUAL\nIJACI\nILESO\nILHAR\nILHAS\nILHEU\nILHOA\nILHOS\nILIBE\nILIBO\nILUDE\nIMAGO\nIMAME\nIMANE\nIMBAU\nIMIGA\nIMIGO\nIMITA\nIMOLE\nIMOLO\nIMOTO\nIMPAR\nIMPIA\nIMPIO\nIMPOR\nIMUNE\nINAJA\nINALA\nINALE\nINALO\nINATO\nINCAR\nINCAS\nINCHA\nINCHE\nINCHO\nINCOA\nINCOE\nINCOO\nINCRA\nINDEX\nINDEZ\nINDIA\nINDIE\nINDIO\nINDOL\nINFRA\nINGAI\nINGUA\nINICA\nINICO\nINUME\nINUMO\nINVES\nIONES\nIONTE\nIPABA\nIPERO\nIPHAN\nIPIAU\nIPIRA\nIPORA\nIPUBI\nIRADA\nIRADO\nIRANI\nIRARA\nIRATI\nIRECE\nIREIS\nIRENE\nIRERE\nIRITE\nIRMAO\nIRMAS\nIROSA\nIROSO\nIRUPI\nISCAR\nISLAO\nISMAR\nISOLA\nISOLE\nISOLO\nISTMO\nITABI\nITACA\nITACO\nITAGI\nITAJA\nITAJE\nITAJU\nITALA\nITALO\nITAPE\nITATI\nITERA\nITERE\nITERO\nITOBI\nITRIO\nIUANE\nIURTA\nIVANA\nIVANO\nIVATE\nIVORA\nIVOTI\nIXORA\nJACIS\nJACOB\nJACRE\nJACTO\nJACUI\nJADER\nJAGOZ\nJAGRA\nJAGRE\nJAIBA\nJAIME\nJAINA\nJAIRA\nJAIRO\nJALES\nJALNE\nJAMBA\nJAMBO\nJAMBU\nJANAL\nJANGA\nJANIO\nJANTA\nJANTE\nJANTO\nJAPAO\nJAQUE\nJARDA\nJARRA\nJARRO\nJASPE\nJATAI\nJATEI\nJAULA\nJAURU\nJAVRA\nJAVRE\nJAVRO\nJAZEM\nJAZER\nJEANE\nJEGRE\nJEGUE\nJEIRA\nJEITO\nJEJUE\nJEJUM\nJEJUO\nJELCO\nJEOVA\nJEQUE\nJERRA\nJERRO\nJESUS\nJETOM\nJETON\nJIADE\nJIGUE\nJIHAD\nJINES\nJIPAO\nJIQUI\nJIRAU\nJOAES\nJOANA\nJOANE\nJOEIS\nJOGAR\nJOGOS\nJOIAS\nJOICA\nJOICE\nJOKER\nJOLDA\nJOLHO\nJONAS\nJONGO\nJONIA\nJONIO\nJOOES\nJORGE\nJORNA\nJORNE\nJORRO\nJOSES\nJOSTA\nJOSUE\nJOTAS\nJOULE\nJOVEM\nJOVES\nJUARA\nJUCAS\nJUCHE\nJUDAS\nJUDEU\nJUDIA\nJUGAR\nJUINA\nJUIZA\nJUIZO\nJULHO\nJULIA\nJULIO\nJUMBO\nJUNCO\nJUNGO\nJUNHO\nJUNIA\nJUNIO\nJUNOS\nJUNTA\nJUNTE\nJUNTO\nJUPIA\nJURAI\nJURAM\nJURAR\nJURAS\nJUREI\nJUREM\nJURES\nJURIS\nJUROS\nJUROU\nJURUA\nJUSAO\nJUSSA\nJUSTA\nJUSTO\nJUTAI\nKANJI\nKARBI\nKARMA\nKASHA\nKAYAK\nKENDO\nKHASI\nKHMER\nKIRIE\nKNYAZ\nKOALA\nKOBAN\nKOINE\nKOMBI\nKONEL\nKRILL\nKUAIT\nKUDZU\nKULAK\nKUMEL\nKUMYK\nKYOTO\nKYRIE\nKYZYL\nLABEU\nLABIA\nLABIL\nLABIO\nLABOR\nLACAO\nLACAR\nLACEI\nLACHO\nLACIO\nLACOS\nLACRE\nLADOS\nLADRA\nLADRE\nLADRO\nLAGAR\nLAGES\nLAGOA\nLAGOS\nLAIAR\nLAICO\nLAIDO\nLAIVO\nLAJES\nLAMAS\nLAMBE\nLAMEN\nLAMIA\nLAMIM\nLAMPO\nLANCA\nLANCE\nLANCO\nLANDE\nLANGE\nLANHA\nLANHO\nLAPAO\nLAPAR\nLAPIA\nLAPIS\nLAPSO\nLAQUE\nLARDO\nLARES\nLAREU\nLARGA\nLARGO\nLARPA\nLARPE\nLARPO\nLARVA\nLASCA\nLASER\nLASSA\nLASSE\nLASSO\nLATAO\nLATAR\nLATAS\nLATER\nLATEX\nLATIA\nLATIM\nLATIR\nLAUDA\nLAUDE\nLAUDO\nLAURA\nLAURO\nLAUTA\nLAUTO\nLAVAI\nLAVAM\nLAVAR\nLAVAS\nLAVEI\nLAVEM\nLAVES\nLAVOR\nLAVOU\nLAVRA\nLAVRE\nLAVRO\nLAZER\nLEBRE\nLEDAS\nLEDOR\nLEDOS\nLEGAL\nLEGAO\nLEGAR\nLEGRA\nLEGUA\nLEGUE\nLEIDE\nLEIGO\nLEILA\nLEINO\nLEIRA\nLEITE\nLEITO\nLEIVA\nLEIXE\nLEIXO\nLEMES\nLENCO\nLENDA\nLENHA\nLENHO\nLENIR\nLENTA\nLENTE\nLENTO\nLEPRA\nLEPTO\nLEQUE\nLERDA\nLERDO\nLERIA\nLERPE\nLESAI\nLESAM\nLESAO\nLESAR\nLESAS\nLESCA\nLESEI\nLESEM\nLESES\nLESIM\nLESMA\nLESME\nLESOU\nLESTA\nLESTE\nLESTO\nLETAL\nLETAO\nLETRA\nLEVAI\nLEVAM\nLEVAR\nLEVAS\nLEVEI\nLEVEL\nLEVEM\nLEVES\nLEVOU\nLEXIA\nLGBTI\nLHAMA\nLHANO\nLIAIS\nLIAME\nLIANA\nLIARA\nLIAVA\nLIBAI\nLIBAM\nLIBAR\nLIBAS\nLIBEI\nLIBEM\nLIBER\nLIBES\nLIBIA\nLIBIO\nLIBOU\nLIBRA\nLIBRE\nLICAO\nLICIO\nLICOR\nLICRA\nLIDAI\nLIDAM\nLIDAR\nLIDAS\nLIDEI\nLIDEM\nLIDER\nLIDES\nLIDIA\nLIDIO\nLIDOU\nLIEIS\nLIGAR\nLIGRE\nLILAS\nLILIA\nLILIO\nLIMAI\nLIMAM\nLIMAO\nLIMAR\nLIMAS\nLIMBO\nLIMEI\nLIMOU\nLIMPA\nLIMPE\nLIMPO\nLINCE\nLINDA\nLINDO\nLINHA\nLINHO\nLINUX\nLIRAS\nLIRIA\nLIRIO\nLISAS\nLISCO\nLISTA\nLITIO\nLITRO\nLIVIA\nLIVIO\nLIVRE\nLIVRO\nLIXAR\nLOBIS\nLOBOS\nLOCAL\nLOCAO\nLOCAR\nLOCAS\nLOCRO\nLOCUS\nLODAO\nLOGAR\nLOGOS\nLOGRA\nLOGRO\nLOICA\nLOIOS\nLOIRA\nLOIRO\nLOLIO\nLOMBA\nLOMBO\nLONCA\nLONGA\nLONGE\nLONGO\nLONJA\nLOPES\nLOQUE\nLORDA\nLORDE\nLORFO\nLORGA\nLORPA\nLOSNA\nLOTAI\nLOTAR\nLOTES\nLOTUS\nLOUCA\nLOUCO\nLOURA\nLOURO\nLOUSA\nLUAIS\nLUANA\nLUARA\nLUAVA\nLUCAS\nLUCIA\nLUCIO\nLUCRO\nLUDRA\nLUDRE\nLUDRO\nLUEIS\nLUGAR\nLUGES\nLUGRE\nLUIGI\nLUISA\nLUITA\nLUMEN\nLUMES\nLUMIA\nLUNAR\nLUQUE\nLUSCO\nLUSOS\nLUTAI\nLUTAM\nLUTAR\nLUTAS\nLUTEI\nLUTEM\nLUTES\nLUTIE\nLUTOU\nLUVAS\nLUXAI\nLUXAM\nLUXAR\nLUXAS\nLUXEI\nLUXEM\nLUXES\nLUXOS\nLUXOU\nLUZES\nLUZIA\nLUZIO\nLUZIR\nLYCRA\nMACAE\nMACAO\nMACAR\nMACAS\nMACAU\nMACHA\nMACHO\nMACIO\nMACOM\nMACRO\nMACUA\nMADRE\nMADRI\nMAFIA\nMAFRA\nMAFUA\nMAGAS\nMAGDA\nMAGIA\nMAGMA\nMAGNA\nMAGNO\nMAGOA\nMAGOS\nMAGRA\nMAGRO\nMAIAS\nMAILU\nMAINE\nMAINO\nMAIOR\nMAIOS\nMAIRA\nMAIRI\nMAIRO\nMAISA\nMAISO\nMAJOR\nMALAR\nMALES\nMALHA\nMALHO\nMALTA\nMALTE\nMALUS\nMALVA\nMALVO\nMAMAE\nMAMAL\nMAMAO\nMAMAR\nMAMBA\nMAMBO\nMAMBU\nMAMIS\nMAMOA\nMANAR\nMANAS\nMANCO\nMANDA\nMANDE\nMANDI\nMANDO\nMANES\nMANGA\nMANGO\nMANHA\nMANIA\nMANIR\nMANJA\nMANSA\nMANSO\nMANTA\nMANTO\nMAOME\nMAORI\nMAPLE\nMARAA\nMARAO\nMARAS\nMARAU\nMARCA\nMARCO\nMARES\nMARGA\nMARIA\nMARIO\nMAROS\nMARRA\nMARTA\nMARTE\nMASER\nMASSA\nMATAI\nMATAM\nMATAO\nMATAR\nMATAS\nMATCH\nMATEI\nMATEM\nMATES\nMATIZ\nMATOU\nMATRI\nMAUES\nMAURA\nMAURO\nMAUSE\nMEADA\nMEADO\nMECHA\nMEDIA\nMEDIO\nMEDIR\nMEDOS\nMEDRA\nMEDRO\nMEIAS\nMEIGA\nMEIGO\nMEIOS\nMEIRE\nMEKEO\nMELAO\nMELAR\nMELES\nMELGA\nMELGO\nMELRA\nMELRO\nMENDA\nMENDO\nMENGO\nMENIR\nMENOR\nMENOS\nMENSO\nMENTA\nMENTE\nMENTO\nMERAR\nMERCA\nMERCE\nMERDA\nMERME\nMERMO\nMESAO\nMESAS\nMESES\nMESMA\nMESMO\nMESON\nMESSE\nMESTA\nMESTO\nMETAL\nMETER\nMETIE\nMETRO\nMEXER\nMIADO\nMIAIS\nMIAMI\nMIARA\nMIAVA\nMICAR\nMICHA\nMICHE\nMICHO\nMICRO\nMIDIA\nMIEIS\nMIGAR\nMIGAS\nMIGRE\nMIGRO\nMIJAI\nMIJAM\nMIJAO\nMIJAR\nMIJAS\nMIJEI\nMIJEM\nMIJES\nMIJOU\nMIKIR\nMILHA\nMILHO\nMIMAR\nMIMIR\nMINAI\nMINAM\nMINAR\nMINAS\nMINEI\nMINEM\nMINES\nMINHA\nMINIX\nMINOS\nMINOU\nMIOJO\nMIOLA\nMIOLO\nMIOMA\nMIOPE\nMIOTO\nMIRAI\nMIRAM\nMIRAR\nMIRAS\nMIREI\nMIREM\nMIRES\nMIRIA\nMIROU\nMIRRA\nMIRTO\nMISSA\nMISSE\nMISSO\nMISTA\nMISTO\nMITIA\nMITIM\nMITRA\nMIUDA\nMIUDO\nMIXER\nMOADA\nMOADO\nMOAIS\nMOCAO\nMOCAR\nMOCAS\nMOCHA\nMOCHE\nMOCHO\nMOCOS\nMODAL\nMODEM\nMODOS\nMODUS\nMOEDA\nMOEGA\nMOEIS\nMOELA\nMOEMA\nMOERA\nMOFAI\nMOFAM\nMOFAR\nMOFAS\nMOFEI\nMOFEM\nMOFES\nMOFOU\nMOGAO\nMOGER\nMOGNO\nMOGOR\nMOIAM\nMOIAS\nMOICO\nMOIDO\nMOINA\nMOIRO\nMOITA\nMOITO\nMOLAR\nMOLDE\nMOLES\nMOLHA\nMOLHE\nMOLHO\nMONCO\nMONDA\nMONGE\nMONHA\nMONHE\nMONHO\nMONIA\nMONIZ\nMONJA\nMONSO\nMONTE\nMONTO\nMORAI\nMORAL\nMORAM\nMORAR\nMORAS\nMORCA\nMORDE\nMOREI\nMOREM\nMORES\nMORIM\nMORMO\nMORNO\nMOROS\nMOROU\nMORRE\nMORRO\nMORSA\nMORSO\nMORTE\nMORTO\nMOSCA\nMOSSA\nMOSTO\nMOTAR\nMOTEL\nMOTIM\nMOTOR\nMOTTA\nMOUCO\nMOURA\nMOURO\nMOUSE\nMOUTA\nMOVEL\nMOVEM\nMOVER\nMOVEU\nMOVIA\nMOXAO\nMUAFO\nMUANA\nMUCIA\nMUCIO\nMUCUM\nMUDAI\nMUDAM\nMUDAR\nMUDAS\nMUDEI\nMUDEM\nMUDES\nMUDEZ\nMUDOS\nMUDOU\nMUDRA\nMUFLA\nMUGIR\nMUITA\nMUITO\nMULSO\nMULTA\nMULTE\nMUMIA\nMUNDE\nMUNDO\nMUNIO\nMUNIR\nMUNTO\nMUNUS\nMUQUE\nMUQUI\nMURAR\nMURCA\nMURCO\nMUROS\nMURRA\nMURRO\nMURTA\nMUSAS\nMUSEU\nMUSGO\nMUSME\nMUSSE\nMUTUA\nMUTUM\nMUTUO\nMUXAO\nNABAL\nNACAO\nNACAR\nNACEM\nNADAI\nNADAM\nNADAR\nNADAS\nNADEI\nNADEM\nNADES\nNADIA\nNADIR\nNADOS\nNADOU\nNAFTA\nNAGAR\nNAIFA\nNAIPE\nNAIRA\nNAITE\nNAKFA\nNALGA\nNALGO\nNANAR\nNANCI\nNANJA\nNAOMI\nNAQUE\nNARIZ\nNAROM\nNASAL\nNASCE\nNASSA\nNATAL\nNATAN\nNATRO\nNATTO\nNAUNS\nNAURU\nNAVAL\nNAVIO\nNECHO\nNECRA\nNEDIA\nNEDIO\nNEGAO\nNEGAR\nNEGRA\nNEGRE\nNEGRO\nNEGUE\nNEHAN\nNELAS\nNELES\nNENEM\nNENIA\nNENTE\nNEPAL\nNEREU\nNERIO\nNERVO\nNESGA\nNESGO\nNESSA\nNESSE\nNESTA\nNESTE\nNETOS\nNEVAI\nNEVAM\nNEVAR\nNEVAS\nNEVEI\nNEVEM\nNEVES\nNEVOA\nNEVOU\nNHAMA\nNHOCA\nNICAR\nNICHA\nNICHO\nNIELO\nNIGER\nNIGUA\nNILAS\nNILOS\nNILZA\nNIMBO\nNIMIO\nNINAI\nNINAM\nNINAR\nNINAS\nNINEI\nNINEM\nNINES\nNINFA\nNINHO\nNINJA\nNINOU\nNIPLE\nNIPOA\nNIQAB\nNISCO\nNISSO\nNISTO\nNITRO\nNIVEA\nNIVEL\nNIVEO\nNIVER\nNOBEL\nNOBRE\nNOCAO\nNODAR\nNODOA\nNOEMI\nNOGAI\nNOIRO\nNOITE\nNOIVA\nNOIVO\nNOJAR\nNOMES\nNONAS\nNONDE\nNONOS\nNORAS\nNORMA\nNORMO\nNORSA\nNORTE\nNOSSA\nNOSSO\nNOTAR\nNOTAS\nNOTEM\nNOTOU\nNOUCA\nNOUTE\nNOVAS\nNOVEL\nNOVES\nNOVOS\nNOXIO\nNUBEA\nNUBEO\nNUBIA\nNUBIL\nNUBIO\nNUCAO\nNUDES\nNUDEZ\nNUELE\nNUELO\nNUMAS\nNUNCA\nNUVEM\nNUVEO\nNUVRE\nNYLON\nOASIS\nOBESO\nOBICE\nOBITO\nOBOES\nOBOLO\nOBRAI\nOBRAM\nOBRAR\nOBRAS\nOBREI\nOBREM\nOBRES\nOBROU\nOBSTA\nOBSTE\nOBSTO\nOBTER\nOBVIO\nOCAPI\nOCARA\nOCASO\nOCTAL\nOCULO\nOCUPA\nODEAO\nODEOM\nODEON\nODIAR\nODIOS\nOESTE\nOFURO\nOGHAM\nOGIVA\nOIRAR\nOITAO\nOITOS\nOLEAR\nOLHAI\nOLHAO\nOLHAR\nOLHOS\nOLIVA\nOLIVE\nOLIVO\nOMBRO\nOMEGA\nOMNIA\nONCAS\nONDAS\nONTEM\nOPACA\nOPACO\nOPALA\nOPCAO\nOPERA\nOPERE\nOPERO\nOPIMO\nOPINE\nOPINO\nOPTAI\nOPTAM\nOPTAR\nOPTAS\nOPTEI\nOPTEM\nOPTES\nOPTOU\nOQUEI\nORACA\nORAIS\nORARA\nORATE\nORAVA\nORCAR\nORDEM\nOREIS\nORFAO\nORFAS\nORFEU\nORGAO\nORGIA\nORIAO\nORIBI\nORINA\nORION\nORIXA\nORLAR\nORNAI\nORNAM\nORNAR\nORNAS\nORNEI\nORNEM\nORNES\nORNIS\nORNOU\nOROBO\nOROCO\nOROMO\nOSACA\nOSAKA\nOSCAR\nOSMAR\nOSMIO\nOSRAM\nOSSEA\nOSSEO\nOSSOS\nOSTRA\nOSTRO\nOTAKU\nOTAVA\nOTICA\nOTICO\nOTIMO\nOTITE\nOUGAR\nOUIJA\nOURAI\nOURAM\nOURAR\nOURAS\nOUREI\nOUREM\nOURES\nOUROS\nOUROU\nOUSAR\nOUSAS\nOUSOU\nOUTAO\nOUTRA\nOUTRO\nOUVIA\nOUVIR\nOUVIU\nOVINO\nOVULO\nOXALA\nOXIDE\nOXIDO\nPABLA\nPABLO\nPACAS\nPACER\nPACOS\nPACTO\nPADEL\nPADRE\nPAEJA\nPAFIA\nPAFIO\nPAFOS\nPAGAO\nPAGAR\nPAGER\nPAGUE\nPAIAL\nPAIBA\nPAICA\nPAINS\nPAIOL\nPAIRE\nPAIRO\nPAIVA\nPAIXA\nPAJEM\nPALAO\nPALAS\nPALAU\nPALCO\nPALHA\nPALIE\nPALIO\nPALMA\nPALME\nPALMO\nPALOP\nPALOR\nPALPO\nPALRA\nPAMPA\nPAMPO\nPANAL\nPANCA\nPANDA\nPANDO\nPANDU\nPANGO\nPANOS\nPANTA\nPAOLA\nPAOLO\nPAPAI\nPAPAL\nPAPAO\nPAPAR\nPAPEL\nPAPER\nPARAI\nPARAM\nPARAR\nPARAS\nPARAU\nPARBA\nPARCA\nPARCO\nPARDO\nPAREA\nPAREI\nPAREM\nPAREO\nPARES\nPARGA\nPARGO\nPARIA\nPARIO\nPARIR\nPARIS\nPARIU\nPARLA\nPAROU\nPARRA\nPARRO\nPARTE\nPARTO\nPARVA\nPARVO\nPASMO\nPASSA\nPASSE\nPASSO\nPASTE\nPASTO\nPATAS\nPATAU\nPATCH\nPATIA\nPATIM\nPATIO\nPATIS\nPATOA\nPATOS\nPATUA\nPAUIS\nPAULA\nPAULO\nPAUSA\nPAUTA\nPAUTE\nPAUTO\nPAVAO\nPAVIA\nPAVIO\nPAVOA\nPAVOR\nPAZES\nPCDOB\nPECAR\nPECAS\nPECHA\nPEDAL\nPEDES\nPEDIA\nPEDIR\nPEDIU\nPEDRA\nPEDRO\nPEGAR\nPEGOU\nPEIDA\nPEIDO\nPEINA\nPEITE\nPEITO\nPEIXA\nPEIXE\nPEJAR\nPELAI\nPELAM\nPELAR\nPELAS\nPELEI\nPELEM\nPELES\nPELEU\nPELOS\nPELOU\nPEMBA\nPENAI\nPENAL\nPENAM\nPENAR\nPENAS\nPENCA\nPENCE\nPENDE\nPENEI\nPENEM\nPENES\nPENHA\nPENIS\nPENNY\nPENOU\nPENSE\nPENSO\nPENTE\nPEOES\nPEONA\nPEQUI\nPERAS\nPERCA\nPERDA\nPERDE\nPERDI\nPERLA\nPERLE\nPERLO\nPERNA\nPERRO\nPERSA\nPERTO\nPERUA\nPERUS\nPESAI\nPESAM\nPESAR\nPESAS\nPESCA\nPESCO\nPESEI\nPESEM\nPESES\nPESOU\nPESTE\nPETAR\nPETIZ\nPETRA\nPEUGA\nPEUVA\nPIABA\nPIADA\nPIALO\nPIANO\nPIARA\nPIATA\nPIAUI\nPIAVA\nPICAO\nPICAR\nPICAS\nPICHA\nPICHE\nPICHO\nPICLE\nPICOS\nPICUI\nPICUM\nPIEIS\nPIEMA\nPIESE\nPIFAO\nPIFAR\nPIFIO\nPILAI\nPILAM\nPILAO\nPILAR\nPILAS\nPILEI\nPILEM\nPILES\nPILHA\nPILHE\nPILHO\nPILIO\nPILOU\nPILUM\nPIMBA\nPINCA\nPINEL\nPINEU\nPINGA\nPINGO\nPINHA\nPINHO\nPINTA\nPINTE\nPINTO\nPIOLA\nPIORA\nPIORE\nPIORO\nPIQUE\nPIQUI\nPIRAI\nPIRAO\nPIRAR\nPIRES\nPIREX\nPIROU\nPIRRO\nPIRUA\nPISAI\nPISAM\nPISAR\nPISAS\nPISCA\nPISCO\nPISEI\nPISEM\nPISES\nPISOU\nPISTA\nPISTO\nPITAO\nPITEU\nPIUMA\nPIVOT\nPIXEL\nPIZZA\nPLACA\nPLAGA\nPLANA\nPLANE\nPLANO\nPLATO\nPLEBE\nPLENA\nPLENO\nPLICA\nPLUMA\nPNEUS\nPOBRE\nPOCAO\nPOCAR\nPOCHA\nPOCOS\nPODAO\nPODAR\nPODEM\nPODER\nPODIA\nPODIO\nPODOA\nPODRE\nPOEJO\nPOEMA\nPOETA\nPOIAL\nPOISA\nPOITA\nPOITE\nPOITO\nPOJAI\nPOJAM\nPOJAR\nPOJAS\nPOJEI\nPOJEM\nPOJES\nPOJOU\nPOLAR\nPOLAS\nPOLCA\nPOLEN\nPOLHO\nPOLIA\nPOLIR\nPOLIS\nPOLOS\nPOLPA\nPOLVO\nPOMAR\nPOMBA\nPOMBO\nPOMES\nPOMOS\nPOMPA\nPONDE\nPONEI\nPONHO\nPONTA\nPONTE\nPONTO\nPOPOS\nPORAO\nPORAS\nPORCA\nPORCO\nPOREM\nPORNO\nPORRA\nPORRE\nPORRO\nPORTA\nPORTE\nPORTO\nPOSAR\nPOSEM\nPOSSA\nPOSSE\nPOSSO\nPOSTA\nPOSTE\nPOSTO\nPOTIM\nPOTRA\nPOTRO\nPOUCA\nPOUCO\nPOULA\nPOULE\nPOULO\nPOUND\nPOUPA\nPOUPE\nPOUPO\nPOUSA\nPOUSO\nPOUTA\nPOVOA\nPOVOS\nPRACA\nPRADO\nPRAGA\nPRAIA\nPRALI\nPRATA\nPRATO\nPRAXA\nPRAXE\nPRAZO\nPREAO\nPREBE\nPRECE\nPRECO\nPREGA\nPREGO\nPREIA\nPREJU\nPRELO\nPRESA\nPRESE\nPRESO\nPRETA\nPRETO\nPREZA\nPREZE\nPREZO\nPRIMA\nPRIMO\nPRIOR\nPROAS\nPROBO\nPROCE\nPROER\nPROFE\nPROIS\nPROLE\nPRONA\nPRONO\nPROSA\nPROTO\nPROVA\nPROVE\nPROVO\nPRUIR\nPRUMO\nPSOAS\nPTDOB\nPTOSE\nPUARA\nPUAVA\nPUBIS\nPUCHA\nPUDER\nPUDIM\nPUDOR\nPUELA\nPUERA\nPUGIL\nPUJAI\nPUJAM\nPUJAR\nPUJAS\nPUJEI\nPUJEM\nPUJES\nPUJOU\nPULAR\nPULGA\nPULGO\nPULHA\nPULSE\nPULSO\nPUNAS\nPUNGA\nPUNHA\nPUNHO\nPUNIR\nPURAS\nPURGA\nPUTAO\nPUTAS\nPUTOS\nPUXAI\nPUXAM\nPUXAO\nPUXAR\nPUXAS\nPUXEI\nPUXEM\nPUXES\nPUXOU\nQATAR\nQUAIS\nQUARE\nQUARK\nQUARO\nQUASE\nQUASI\nQUATA\nQUATI\nQUECA\nQUEDA\nQUEDE\nQUEDO\nQUEPE\nQUERO\nQUETO\nQUIBE\nQUICA\nQUICO\nQUILO\nQUINA\nQUIPA\nQUITE\nQUITO\nQUIUI\nQUIVI\nQUIXO\nQUOTA\nRABAO\nRABAT\nRABAZ\nRABIL\nRACAO\nRACAS\nRACHA\nRACHE\nRACHO\nRACUM\nRADAO\nRADAR\nRADIO\nRADON\nRAFAI\nRAFAM\nRAFAR\nRAFAS\nRAFEI\nRAFEM\nRAFES\nRAFOU\nRAIAL\nRAIAR\nRAIDE\nRAION\nRAIOS\nRAIVA\nRAJAR\nRALAI\nRALAM\nRALAR\nRALAS\nRALEI\nRALEM\nRALES\nRALHO\nRALLY\nRALOU\nRAMAI\nRAMAL\nRAMAM\nRAMAO\nRAMAR\nRAMAS\nRAMEI\nRAMEM\nRAMES\nRAMOS\nRAMOU\nRAMPA\nRANCA\nRANCO\nRANGO\nRANHO\nRAPAI\nRAPAM\nRAPAR\nRAPAS\nRAPAZ\nRAPEI\nRAPEL\nRAPEM\nRAPES\nRAPOU\nRAPTO\nRAQUE\nRASAR\nRASAS\nRASCA\nRASGA\nRASGO\nRASOS\nRASPA\nRASPE\nRASPO\nRASTO\nRATAI\nRATAM\nRATAO\nRATAR\nRATAS\nRATEI\nRATEM\nRATES\nRATOS\nRATOU\nRAZAO\nRAZIA\nREACA\nREAIS\nREBAR\nREBOO\nRECEM\nRECHO\nRECTA\nRECTO\nRECUA\nRECUO\nREDEA\nREDES\nREDIL\nREDOR\nREFEM\nREFIL\nREFRI\nREGAI\nREGAL\nREGAR\nREGAS\nREGER\nREGIA\nREGIO\nREGIS\nREGRA\nREGRE\nREGRO\nREGUA\nREIDE\nREIMA\nREINE\nREINO\nREJAO\nRELAO\nRELAR\nRELER\nRELES\nRELHA\nRELHO\nRELUZ\nRELVA\nREMAI\nREMAM\nREMAR\nREMAS\nREMEI\nREMEM\nREMES\nREMIR\nREMIX\nREMOA\nREMOI\nREMOO\nREMOS\nREMOU\nRENAL\nRENAN\nRENCA\nRENDA\nRENDE\nRENGO\nRENIO\nRENTE\nREPOR\nREPOS\nREPTO\nRESMA\nRESTE\nRESTO\nRETAS\nRETER\nRETOS\nRETRO\nREUSO\nREVEL\nREVER\nREVES\nREXIO\nREZAI\nREZAM\nREZAR\nREZAS\nREZEI\nREZEM\nREZES\nREZOU\nRIADE\nRIANA\nRIBAS\nRICAR\nRICAS\nRICOS\nRIFAO\nRIFAR\nRIFLE\nRIFTE\nRIGOR\nRIJAO\nRIJAR\nRIJOS\nRILEX\nRIMAI\nRIMAM\nRIMAR\nRIMAS\nRIMEI\nRIMEL\nRIMEM\nRIMES\nRIMOU\nRINDO\nRINHA\nRINSO\nRIPAR\nRISCA\nRISCO\nRISSE\nRISTE\nRITAO\nRITAS\nRITMO\nRITOS\nRIUTA\nRIVAL\nROBER\nROBOT\nROCAR\nROCAS\nROCAZ\nROCHA\nROCIA\nROCIE\nROCIM\nROCIO\nRODAI\nRODAM\nRODAR\nRODAS\nRODEI\nRODEM\nRODES\nRODIO\nRODOU\nROGAR\nROGUE\nROIAM\nROIDO\nROJAI\nROJAM\nROJAO\nROJAR\nROJAS\nROJEI\nROJEM\nROJES\nROJOS\nROJOU\nROLAI\nROLAM\nROLAO\nROLAR\nROLAS\nROLDA\nROLDE\nROLDO\nROLEI\nROLEM\nROLES\nROLHA\nROLHE\nROLHO\nROLOU\nROMAI\nROMAM\nROMAO\nROMAR\nROMAS\nROMBO\nROMEI\nROMEM\nROMES\nROMEU\nROMOU\nRONCO\nRONDA\nRONDE\nRONDO\nRONHA\nROQUE\nROSAI\nROSAL\nROSAM\nROSAR\nROSAS\nROSCA\nROSEA\nROSEI\nROSEM\nROSEO\nROSES\nROSNA\nROSNE\nROSNO\nROSOS\nROSOU\nROSTO\nROTAR\nROUBA\nROUBE\nROUBO\nROUCA\nROUCO\nROUPA\nROXAS\nROXOS\nRUADA\nRUBIA\nRUBIM\nRUBIS\nRUBLO\nRUBOR\nRUBRA\nRUBRO\nRUDOS\nRUELA\nRUFAR\nRUFIE\nRUFIO\nRUFOS\nRUGBI\nRUGBY\nRUGIR\nRUIDE\nRUIDO\nRUINA\nRUIVO\nRUMAI\nRUMAM\nRUMAR\nRUMAS\nRUMBA\nRUMEI\nRUMEM\nRUMES\nRUMOR\nRUMOU\nRUPIA\nRURAL\nRUSGA\nRUSSA\nRUSSO\nRUTAR\nRUTES\nSAAMI\nSABAO\nSABEI\nSABER\nSABIA\nSABIO\nSABLE\nSABOR\nSABRA\nSABRE\nSACAL\nSACAR\nSACHO\nSACIE\nSACIO\nSACRA\nSACRO\nSACUE\nSADRA\nSAETA\nSAFAR\nSAFOS\nSAFRA\nSAGAZ\nSAGRA\nSAGRE\nSAGRO\nSAGUI\nSAHEL\nSAIAM\nSAIAS\nSAIBA\nSAIBO\nSAIDA\nSAIDO\nSAIRE\nSAITE\nSALAI\nSALAO\nSALAR\nSALAS\nSALAZ\nSALDO\nSALEI\nSALEM\nSALES\nSALGA\nSALHO\nSALOA\nSALOU\nSALSA\nSALSO\nSALTA\nSALTE\nSALTO\nSALVA\nSALVE\nSALVO\nSAMBA\nSAMIO\nSAMOA\nSAMPA\nSANCA\nSANDE\nSANDO\nSANEI\nSANGA\nSANHA\nSANIE\nSANJA\nSANTA\nSANTO\nSAPAO\nSAPOS\nSAQUE\nSARAI\nSARAM\nSARAO\nSARAR\nSARAS\nSARAU\nSARCA\nSARDA\nSARDO\nSAREI\nSAREM\nSARES\nSARGA\nSARJA\nSARNA\nSAROS\nSAROU\nSARRA\nSARRO\nSARTA\nSATAO\nSAUCO\nSAUDE\nSAUIS\nSAULA\nSAULO\nSAUNA\nSAUVA\nSAVEL\nSAVIA\nSAVIO\nSAXAO\nSAZAO\nSCHWA\nSCIFI\nSCOUT\nSEARA\nSECAO\nSECAR\nSECOS\nSEDAN\nSEDAS\nSEDES\nSEDEX\nSEDIA\nSEGAR\nSEGRE\nSEGUE\nSEIOS\nSEIRA\nSEITA\nSEIVA\nSEIXO\nSEJAM\nSEJAS\nSELAR\nSELEM\nSELHA\nSELIC\nSELIM\nSELVA\nSEMEN\nSEMIS\nSENAO\nSENAS\nSENDA\nSENDO\nSENHA\nSENIL\nSENIO\nSENNA\nSENRA\nSENSO\nSENTE\nSENTO\nSEPIA\nSERAO\nSERAS\nSEREI\nSEREM\nSERES\nSERIA\nSERIE\nSERIO\nSERPA\nSERRA\nSERRE\nSERRO\nSERTA\nSERVA\nSERVE\nSERVI\nSERVO\nSESGO\nSESMA\nSESSO\nSESTA\nSETAR\nSETAS\nSETES\nSETOR\nSEVAI\nSEVAM\nSEVAR\nSEVAS\nSEVEI\nSEVEM\nSEVES\nSEVOU\nSEXOS\nSEXTA\nSEXTO\nSHAPE\nSHEIK\nSHOJO\nSHORT\nSHOYU\nSICLO\nSIDAS\nSIDOS\nSIDRA\nSIENE\nSIFAO\nSIGLA\nSIGMA\nSIGNO\nSILAS\nSILEX\nSILFO\nSILHA\nSILTE\nSILVA\nSIMAO\nSIMIL\nSIMIO\nSINAL\nSINAO\nSINDI\nSINES\nSINHA\nSINOP\nSINTA\nSINTO\nSIOUX\nSIPAM\nSIQUE\nSIRGA\nSIRGO\nSIRIA\nSIRIM\nSIRIO\nSIRTE\nSIRVO\nSISMO\nSISTA\nSISTO\nSITIO\nSIVAM\nSKATE\nSKIFF\nSLACK\nSLANG\nSLICK\nSLIDE\nSNACK\nSOADA\nSOADO\nSOAIS\nSOARA\nSOAVA\nSOBEM\nSOBES\nSOBEU\nSOBPE\nSOBRA\nSOBRE\nSOCAR\nSOCIO\nSODIO\nSOEIS\nSOERA\nSOFAS\nSOFIA\nSOFIO\nSOFRE\nSOGRA\nSOGRO\nSOIAM\nSOIAS\nSOITO\nSOLAO\nSOLAR\nSOLAZ\nSOLDA\nSOLDE\nSOLDO\nSOLES\nSOLHA\nSOLHO\nSOLIO\nSOLTA\nSOLTE\nSOLTO\nSOMAI\nSOMAM\nSOMAR\nSOMAS\nSOMEI\nSOMEM\nSOMES\nSOMOS\nSOMOU\nSONAR\nSONDA\nSONHA\nSONHE\nSONHO\nSONSA\nSONSO\nSOPAS\nSOPOR\nSOPRE\nSOPRO\nSORCA\nSORGO\nSORNA\nSOROR\nSOROS\nSORTE\nSORVA\nSORVO\nSOSIA\nSOSSO\nSOTAO\nSOUBE\nSOURE\nSOUSA\nSOUTA\nSOUTO\nSOUZA\nSOVAI\nSOVAM\nSOVAR\nSOVAS\nSOVEI\nSOVEM\nSOVES\nSOVOU\nSPORT\nSPRAY\nSPRUE\nSTAFF\nSTAND\nSTICK\nSTOCK\nSTOLL\nSTOUT\nSUABE\nSUADO\nSUAVE\nSUAZI\nSUBAM\nSUBAS\nSUBIA\nSUBIR\nSUBIS\nSUBIU\nSUCHO\nSUCIA\nSUCRE\nSUDAM\nSUDAO\nSUDRO\nSUECO\nSUELI\nSUETO\nSUEVO\nSUFLE\nSUGAR\nSUICA\nSUICO\nSUINO\nSUITE\nSUJAI\nSUJAM\nSUJAR\nSUJAS\nSUJEI\nSUJEM\nSUJES\nSUJOS\nSUJOU\nSULCO\nSUMIR\nSUNAB\nSUNDA\nSUPER\nSUPOR\nSUPRE\nSURCA\nSURDA\nSURDO\nSURFE\nSURFO\nSURRA\nSURRO\nSURTO\nSUSAO\nSUSAS\nSUSHI\nSUSSA\nSUSTA\nSUSTE\nSUSTO\nSUTIA\nSUTIL\nSUTIS\nSWING\nTABAI\nTABAO\nTABUA\nTABUS\nTACAR\nTACHA\nTACHE\nTACHO\nTAFUL\nTAIER\nTAIGA\nTAIKO\nTAINA\nTAIPA\nTAIPU\nTAISE\nTAIUA\nTALAI\nTALAM\nTALAO\nTALAR\nTALAS\nTALCO\nTALEI\nTALEM\nTALES\nTALHA\nTALHE\nTALHO\nTALIA\nTALIM\nTALIO\nTALOU\nTAMAO\nTAMEM\nTAMIL\nTAMOS\nTAMPA\nTAMPE\nTAMPO\nTANAS\nTANCA\nTANGA\nTANGE\nTANGO\nTANHA\nTANHO\nTANSA\nTANSO\nTANTA\nTANTO\nTAOCA\nTAPAI\nTAPAM\nTAPAR\nTAPAS\nTAPEI\nTAPEM\nTAPES\nTAPIR\nTAPOU\nTAPUA\nTARAR\nTARDA\nTARDE\nTARDO\nTARJA\nTAROT\nTARRO\nTARSO\nTASCA\nTASCO\nTASTO\nTATIL\nTATUI\nTAVAO\nTAXAI\nTAXAM\nTAXAR\nTAXAS\nTAXEI\nTAXEM\nTAXES\nTAXIS\nTAXON\nTAXOU\nTCHAU\nTEBAS\nTECER\nTECLA\nTECLE\nTECLO\nTECNO\nTECTO\nTEDIO\nTEERA\nTEIGA\nTEIMA\nTEIME\nTEIMO\nTEIPE\nTEITO\nTEIXO\nTELAI\nTELAM\nTELAO\nTELAR\nTELAS\nTELEI\nTELEM\nTELES\nTELEX\nTELHA\nTELHO\nTELOS\nTELOU\nTEMAO\nTEMAS\nTEMBA\nTEMER\nTEMIA\nTEMOR\nTEMOS\nTEMPO\nTENAZ\nTENCA\nTENDA\nTENDE\nTENDO\nTENHA\nTENHO\nTENIA\nTENIS\nTENOR\nTENRA\nTENRO\nTENSA\nTENSO\nTENTA\nTENTE\nTENTO\nTENUE\nTERAO\nTERAS\nTERCA\nTERCO\nTEREI\nTERGO\nTERIA\nTERMA\nTERMO\nTERNA\nTERNO\nTERRA\nTERSO\nTESAI\nTESAM\nTESAO\nTESAR\nTESAS\nTESEI\nTESEM\nTESES\nTESEU\nTESLA\nTESOU\nTESSE\nTESTA\nTESTE\nTESTO\nTETAS\nTETIS\nTETRA\nTETRO\nTETUM\nTEUDO\nTEUTO\nTEXAS\nTEXTO\nTIAGA\nTIAGO\nTIARA\nTIBAR\nTIBAU\nTIBIO\nTIBUM\nTICAO\nTICAR\nTIDAS\nTIDOS\nTIETE\nTIFEU\nTIGRE\nTILAR\nTILIA\nTILTE\nTIMAO\nTIMBA\nTIMBO\nTIMER\nTIMON\nTINER\nTINGE\nTINHA\nTINIR\nTINTA\nTINTO\nTIPLE\nTIPOI\nTIQUE\nTIRAI\nTIRAM\nTIRAR\nTIRAS\nTIREI\nTIREM\nTIRES\nTIROS\nTIROU\nTIRSO\nTISNE\nTISSO\nTISSU\nTITIA\nTITIO\nTIVER\nTMESE\nTOADA\nTOARI\nTOCAM\nTOCAR\nTOCHA\nTOCHO\nTOCOU\nTODAS\nTODOS\nTOGAR\nTOICA\nTOIRA\nTOIRO\nTOJAL\nTOKEN\nTOKYO\nTOLAR\nTOLDA\nTOLDO\nTOLHO\nTOMAI\nTOMAM\nTOMAR\nTOMAS\nTOMBA\nTOMBE\nTOMBO\nTOMEI\nTOMEM\nTOMES\nTOMOS\nTOMOU\nTONAL\nTONAR\nTONAS\nTONEL\nTONER\nTONGA\nTONHO\nTONTA\nTONTO\nTOPAI\nTOPAM\nTOPAR\nTOPAS\nTOPEI\nTOPEM\nTOPES\nTOPOU\nTOQUE\nTORAL\nTORAR\nTORAX\nTORCO\nTORDA\nTORDO\nTORGA\nTORGO\nTORIO\nTORNA\nTORNE\nTORNO\nTORPE\nTORRE\nTORSO\nTORTA\nTORTO\nTORVO\nTOSAR\nTOSCO\nTOSSE\nTOSTA\nTOSTE\nTOTAL\nTOTOS\nTOUCA\nTOUCO\nTOUPA\nTOURA\nTOURO\nTOUTA\nTOXIA\nTRACA\nTRACE\nTRACO\nTRAEM\nTRAGO\nTRAIR\nTRAJE\nTRAJO\nTRAMA\nTRAME\nTRAPO\nTRARA\nTRASH\nTRATA\nTRATE\nTRATO\nTRAVE\nTRAVO\nTRECO\nTRELA\nTREMA\nTREME\nTREMI\nTRENA\nTRENO\nTRENS\nTREPA\nTREPE\nTREPO\nTRESA\nTRETA\nTREUS\nTREVA\nTREVO\nTREZE\nTRIAL\nTRIBO\nTRICA\nTRICK\nTRIGA\nTRIGO\nTRILO\nTRINA\nTRINO\nTRIOS\nTRIPA\nTRIPE\nTROAR\nTROBA\nTROCA\nTROCO\nTROFA\nTROIA\nTROLE\nTROLL\nTRONO\nTROPA\nTROPO\nTROTE\nTROVA\nTRUAO\nTRUCO\nTRUFA\nTRUTA\nTUACA\nTUBAS\nTUBOS\nTUFAO\nTUGIR\nTUGUE\nTUIDE\nTUITE\nTULHA\nTULIA\nTULIO\nTUMBA\nTUMOR\nTUNAR\nTUNAS\nTUNDA\nTUNEL\nTUNES\nTUNGA\nTUNIS\nTUPAS\nTUPIS\nTURBA\nTURBE\nTURBO\nTURCO\nTURFA\nTURMA\nTURNE\nTURNO\nTURUQ\nTURVE\nTURVO\nTUSCO\nTUTAI\nTUTAM\nTUTAR\nTUTAS\nTUTEI\nTUTEM\nTUTES\nTUTIA\nTUTOR\nTUTOU\nTUTUM\nTWEED\nTWIST\nUAICA\nUAMBE\nUAURA\nUBAIA\nUBATA\nUBERE\nUCCLA\nUCHOA\nUFANO\nUFRGS\nUIBAI\nUISTE\nUIVAI\nUIVAM\nUIVAR\nUIVAS\nUIVEI\nUIVEM\nUIVES\nUIVOS\nUIVOU\nULEAR\nULPIA\nULPIO\nULULA\nULULE\nULULO\nUMAMI\nUMARI\nUMBRO\nUMERO\nUMIDO\nUNCAO\nUNGIR\nUNHAR\nUNHAS\nUNHOS\nUNIAO\nUNICA\nUNICO\nUNIDA\nUNIDO\nUNTAR\nUOLOF\nURANO\nURDIA\nURDIR\nUREIA\nURGIR\nURIBE\nURICO\nURINA\nURINE\nURINO\nURNAS\nURRAR\nURRUL\nURSAS\nURSOS\nURUBU\nURUPA\nURUPE\nURUTU\nURZAL\nUSADA\nUSADO\nUSAIS\nUSARA\nUSAVA\nUSEIS\nUSINA\nUSMAR\nUSTRA\nUSUAL\nUSURA\nUTERO\nUVAIA\nUVIAR\nUVULA\nUYEZD\nVACAO\nVACAR\nVACUA\nVACUM\nVACUO\nVADIA\nVADIO\nVADUZ\nVAGAO\nVAGAR\nVAGEM\nVAGIR\nVAGOS\nVAIAR\nVAIBE\nVALAO\nVALAR\nVALAS\nVALDA\nVALDO\nVALER\nVALES\nVALHA\nVALIA\nVALOR\nVALSA\nVALSE\nVALSO\nVALVA\nVAMOS\nVAMPE\nVANDA\nVANDO\nVANIA\nVANIO\nVAPOR\nVARAI\nVARAM\nVARAO\nVARAR\nVARAS\nVAREI\nVAREM\nVARES\nVARGA\nVARGE\nVARIA\nVARIO\nVARIZ\nVARJA\nVAROU\nVASCO\nVASOS\nVASTO\nVATEL\nVATES\nVATIO\nVAZAI\nVAZAM\nVAZAO\nVAZAR\nVAZAS\nVAZEI\nVAZEM\nVAZES\nVAZIA\nVAZIO\nVAZOU\nVEADA\nVEADO\nVEDAR\nVEDES\nVEDOR\nVEDRA\nVEDRO\nVEGAN\nVEIAS\nVEIGA\nVEIRO\nVEJAM\nVELAI\nVELAM\nVELAR\nVELAS\nVELEI\nVELEM\nVELES\nVELHA\nVELHO\nVELOU\nVELOZ\nVENAL\nVENCE\nVENCI\nVENDA\nVENDE\nVENDO\nVENHA\nVENHO\nVENIA\nVENTA\nVENTO\nVENUS\nVEPSA\nVEPSO\nVERAO\nVERAS\nVERAZ\nVERBA\nVERBO\nVERCA\nVERDE\nVEREI\nVERGA\nVERGE\nVERIA\nVERME\nVEROS\nVERSO\nVERTE\nVESGO\nVESPA\nVESTA\nVESTE\nVESTI\nVETAO\nVETAR\nVETOR\nVEXAR\nVEZES\nVIADA\nVIADO\nVIAJA\nVIAJE\nVIAJO\nVIANA\nVIBRA\nVICIO\nVIDAR\nVIDAS\nVIDEO\nVIDES\nVIDRA\nVIDRO\nVIELA\nVIENA\nVIERA\nVIGAR\nVIGER\nVIGIA\nVIGIE\nVIGIL\nVIGIO\nVIGOR\nVILAO\nVILAS\nVILOA\nVIMOS\nVINCA\nVINCO\nVINDA\nVINDE\nVINDO\nVINGA\nVINHA\nVINHO\nVINIL\nVINIS\nVINTE\nVIOLA\nVIRAI\nVIRAL\nVIRAM\nVIRAO\nVIRAR\nVIRAS\nVIREI\nVIREM\nVIRES\nVIRGO\nVIRIA\nVIRIL\nVIRIS\nVIROL\nVIROU\nVIRUS\nVISAO\nVISAR\nVISCO\nVISEU\nVISGO\nVISOM\nVISON\nVISSE\nVISTA\nVISTE\nVISTO\nVITAL\nVITOR\nVIUVA\nVIUVO\nVIVAS\nVIVAZ\nVIVER\nVIVIA\nVIVOS\nVOAVA\nVOCAL\nVOCES\nVOCLP\nVODCA\nVODKA\nVOEJA\nVOGAL\nVOGAM\nVOGAR\nVOGAS\nVOILE\nVOLEI\nVOLPE\nVOLPS\nVOLTA\nVOLTE\nVOLTO\nVOLTS\nVOMER\nVORAZ\nVOSCO\nVOSSA\nVOSSO\nVOTAI\nVOTAM\nVOTAR\nVOTAS\nVOTEI\nVOTEM\nVOTES\nVOTOS\nVOTOU\nVOUGO\nVOZES\nVULGO\nVULTO\nVULVA\nVURMO\nWAIKA\nWOLOF\nXABRE\nXACHO\nXACRA\nXADOR\nXAILE\nXAMAS\nXAMPU\nXANGO\nXARDA\nXARDO\nXAREL\nXAREM\nXARIA\nXAUAL\nXAXAR\nXAXIM\nXEBRA\nXEBRE\nXEICA\nXELIM\nXENAO\nXENIO\nXENON\nXEQUE\nXEREM\nXEREZ\nXEROX\nXERPA\nXEXEU\nXHOSA\nXIITA\nXIMBE\nXINGA\nXINTO\nXISTA\nXISTO\nXOCAR\nXOFAR\nXOFRE\nXOGUM\nXOPIM\nXORCA\nXORDO\nXORTE\nXOTAI\nXOTAM\nXOTAR\nXOTAS\nXOTEI\nXOTEM\nXOTES\nXOTOU\nXOUVA\nXUATE\nXUCRO\nXURRO\nYACHT\nZAGAL\nZAGRE\nZAINO\nZAIRA\nZAIRO\nZAMBA\nZAMBI\nZAMBO\nZANGA\nZANZE\nZANZO\nZAPAR\nZAVAR\nZAZAO\nZEBRA\nZEBRO\nZEILA\nZELAM\nZELAR\nZELIA\nZELIO\nZELOS\nZERAR\nZEROS\nZEUGO\nZICHA\nZICHO\nZINCO\nZINGA\nZINIR\nZIPAI\nZIPAM\nZIPAR\nZIPAS\nZIPEI\nZIPEM\nZIPER\nZIPES\nZIPOU\nZLOTI\nZLOTY\nZOEIA\nZOIAO\nZOICO\nZOILO\nZOIRA\nZOMBA\nZOMBE\nZOMBO\nZONAR\nZONCA\nZORRA\nZORRO\nZOUPO\nZOURA\nZUATE\nZUCAR\nZUELA\nZUMBI\nZUNGE\nZUNIA\nZUNIR\nZUPAR\nZURRA\nZURRE\nZURRO\nZURUO\nZURZA"
  },
  {
    "path": "core/src/main/res/values/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#6040D7</color>\n</resources>"
  },
  {
    "path": "core/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">NewQuiz</string>\n    <string name=\"quick_quiz\">Quick Quiz</string>\n    <string name=\"back\">Back</string>\n    <string name=\"more_options\">More options</string>\n    <string name=\"saved_questions\">Saved questions</string>\n    <string name=\"wordle\">Wordle</string>\n    <string name=\"wordle_infinite\">Wordle infinite</string>\n    <string name=\"wordle_daily\">Daily wordle</string>\n    <string name=\"verify\">Verify</string>\n    <string name=\"play_again\">Play again</string>\n    <string name=\"close\">Close</string>\n    <string name=\"game_over\">Game Over</string>\n    <string name=\"item_empty\">Item empty</string>\n    <string name=\"item_i_none\">Item %1$s none</string>\n    <string name=\"item_i_present\">Item %1$s present</string>\n    <string name=\"item_i_correct\">Item %1$s correct</string>\n    <string name=\"four_letters\">4 Letters</string>\n    <string name=\"five_letters\">5 Letters</string>\n    <string name=\"six_letters\">6 Letters</string>\n    <string name=\"back_month\">Back month</string>\n    <string name=\"next_month\">Next month</string>\n    <string name=\"settings\">Settings</string>\n    <string name=\"general\">General</string>\n    <string name=\"quiz\">Quiz</string>\n    <string name=\"quiz_language\">Quiz language</string>\n    <string name=\"english\">English</string>\n    <string name=\"portuguese\">Portuguese</string>\n    <string name=\"spanish\">Spanish</string>\n    <string name=\"french\">French</string>\n    <string name=\"color_blind_mode\">Color blind mode</string>\n    <string name=\"high_contrast_colors\">High contrast colors</string>\n    <string name=\"info\">Info</string>\n    <string name=\"letter_hints\">Letter hints</string>\n    <string name=\"hint_above_the_letter_that_it_appears_twice_or_more_in_the_hidden_word\">Hint above the letter that it appears twice or more in the hidden word</string>\n    <string name=\"hard_mode\">Hard mode</string>\n    <string name=\"any_revealed_hints_must_be_used_in_subsequest_guesses\">Any revealed hints must be used in subsequent guesses</string>\n    <string name=\"rows_limited\">Rows limited</string>\n    <string name=\"wordle_infinite_row_limited\">Wordle infinite row limited.</string>\n    <string name=\"wordle_infinite_row_limit_value\">Wordle infinite row limit value.</string>\n    <string name=\"clean_calendar_data\">Clean calendar data</string>\n    <string name=\"clean_saved_calendar_wins_losses\">Cleans all saved calendar wins/losses.</string>\n    <string name=\"clear_settings\">Clear Preferences</string>\n    <string name=\"remove_all_saved_settings\">Remove all saved settings</string>\n    <string name=\"home\">Home</string>\n    <string name=\"today_game\">Today game</string>\n    <string name=\"today_random_game\">Today random game</string>\n    <string name=\"multi_choice_quiz\">Multi choice quiz</string>\n    <string name=\"flag_quiz\">Flag quiz</string>\n    <string name=\"logo_quiz\">Logo quiz</string>\n    <string name=\"other\">Other</string>\n    <string name=\"general_knowledge\">General knowledge</string>\n    <string name=\"entertainment_books\">Books</string>\n    <string name=\"entertainment_film\">Film</string>\n    <string name=\"entertainment_music\">Music</string>\n    <string name=\"entertainment_musicals_and_theatres\">Musicals &amp; Theatres</string>\n    <string name=\"entertainment_television\">Television</string>\n    <string name=\"entertainment_video_games\">Video Games</string>\n    <string name=\"entertainment_board_games\">Board Games</string>\n    <string name=\"science_and_nature\">Science &amp; Nature</string>\n    <string name=\"science_computers\">Computers</string>\n    <string name=\"science_mathematics\">Mathematics</string>\n    <string name=\"mythology\">Mythology</string>\n    <string name=\"sports\">Sports</string>\n    <string name=\"geography\">Geography</string>\n    <string name=\"history\">History</string>\n    <string name=\"politics\">Politics</string>\n    <string name=\"art\">Art</string>\n    <string name=\"celebrities\">Celebrities</string>\n    <string name=\"animals\">Animals</string>\n    <string name=\"vehicles\">Vehicles</string>\n    <string name=\"entertainment_comics\">Comics</string>\n    <string name=\"science_gadgets\">Gadgets</string>\n    <string name=\"entertainment_japanese_anime_and_manga\">Japanese Anime &amp; Manga</string>\n    <string name=\"entertainment_cartoon_and_animations\">Cartoon &amp; Animations</string>\n    <string name=\"categories\">Categories</string>\n    <plurals name=\"n_questions_available\">\n        <item quantity=\"zero\">No questions available</item>\n        <item quantity=\"one\">%1$s question available</item>\n        <item quantity=\"other\">%1$s questions available</item>\n    </plurals>\n    <string name=\"see_all\">See all</string>\n    <string name=\"difficulty\">Difficulty</string>\n    <string name=\"is_not_in_the_target_word_wordle\">\" is not in the target word.\"</string>\n    <string name=\"is_in_the_word_but_in_the_wrong_spot_wordle\">\" is in the word but in the wrong spot.\"</string>\n    <string name=\"is_in_the_word_and_in_the_correct_spot_wordle\">\" is in the word and in the correct spot.\"</string>\n    <string name=\"results_screen\">Results screen</string>\n    <string name=\"n_correct_questions\">%1$s correct questions</string>\n    <string name=\"quiz_question_size\">Quiz question size</string>\n    <string name=\"translation\">Translation</string>\n    <string name=\"translation_enabled\">Translation enabled</string>\n    <string name=\"download_translation_model\">Download translation model</string>\n    <string name=\"download_translation_model_description\">Download translation model for on device translation.</string>\n    <string name=\"downloading_translation_model\">Downloading translation model</string>\n    <string name=\"downloading_translation_model_description\">Downloading translation model, this may take a while.</string>\n    <string name=\"delete_translation_model\">Delete translation model</string>\n    <string name=\"online\">Online</string>\n    <string name=\"profile\">Profile</string>\n    <string name=\"correct_answers\">Correct answers</string>\n    <string name=\"total_questions\">Total questions</string>\n    <string name=\"last_quiz_times\">Last quiz times</string>\n    <string name=\"time\">Time</string>\n    <string name=\"last_questions\">Last questions</string>\n    <string name=\"correct_words\">Correct words</string>\n    <string name=\"total_words\">Total words</string>\n    <string name=\"user_xp\">User XP</string>\n    <string name=\"level\">Level</string>\n    <string name=\"current_xp\">Current XP</string>\n    <string name=\"required_xp_to_next_level\">Required xp to next level</string>\n    <string name=\"photo_of_s\">Photo of %1$s</string>\n    <string name=\"good_morning\">Good morning</string>\n    <string name=\"good_afternoon\">Good afternoon</string>\n    <string name=\"good_evening\">Good evening</string>\n    <string name=\"good_night\">Good night</string>\n    <string name=\"no_diamonds\">No diamonds</string>\n    <string name=\"skip_question_q\">Skip question?</string>\n    <plurals name=\"you_have_n_diamonds_skip_question_q\">\n        <item quantity=\"one\">You have one diamond, do you want to use the diamond to skip this question?</item>\n        <item quantity=\"other\">You have %1$d diamonds, do you want to use %2$d diamonds to skip this question?</item>\n    </plurals>\n    <string name=\"no_diamonds_to_skip_question_description\">You don\\'t have diamonds to skip this question!</string>\n    <string name=\"skip\">Skip</string>\n    <string name=\"save\">Save</string>\n    <string name=\"dismiss\">Dismiss</string>\n    <string name=\"guess_the_word\">Guess the Word</string>\n    <string name=\"guess_the_number\">Guess the Number</string>\n    <string name=\"guess_math_formula\">Guess math formula</string>\n    <string name=\"guess_solution\">Guess solution</string>\n    <string name=\"math_quiz\">Math quiz</string>\n    <string name=\"maze\">Maze</string>\n    <string name=\"easy\">Easy</string>\n    <string name=\"medium\">Medium</string>\n    <string name=\"hard\">Hard</string>\n    <string name=\"open_drawer\">Open drawer</string>\n    <string name=\"copy_maze_seed\">Copy maze seed</string>\n    <string name=\"restart_maze\">Restart maze</string>\n    <string name=\"generate_maze\">Generate maze</string>\n    <string name=\"maze_completed\">Maze completed!</string>\n    <string name=\"generate_offline_maze\">Generate offline maze</string>\n    <string name=\"generate_offline_maze_description\">Generate maze with all game modes that don\\'t need internet connection.</string>\n    <string name=\"random_maze\">Random maze</string>\n    <string name=\"generate_maze_with_random_items\">Generate maze with random questions, all game modes will be included.</string>\n    <string name=\"custom_maze\">Custom maze</string>\n    <string name=\"generate_maze_with_custom_options\">Generate maze with custom options</string>\n    <string name=\"expand_custom_options\">Expand custom options</string>\n    <string name=\"custom_maze_multi_choice_warning\">When using custom seed, multi choice questions will not be random generated by this seed.</string>\n    <string name=\"generate\">Generate</string>\n    <string name=\"seed\">Seed</string>\n    <string name=\"user\">User</string>\n    <string name=\"number_trivia\">Number trivia</string>\n    <string name=\"about_and_help\">About and Help</string>\n    <string name=\"animations\">Animations</string>\n    <string name=\"animations_enabled\">Animations Enabled</string>\n    <string name=\"global_animations_enabled\">Global animations enabled</string>\n    <string name=\"wordle_animations_enabled\">Wordle animations enabled</string>\n    <string name=\"wordle_animations_enabled_description\">Enable animations for wordle game mode.</string>\n    <string name=\"multi_choice_animations_enabled\">Multi choice animations enabled</string>\n    <string name=\"multi_choice_animations_enabled_description\">Enable animations for multi choice game mode.</string>\n    <string name=\"sort_questions\">Sort questions</string>\n    <string name=\"play_with_random_saved_questions\">Play quiz with random saved questions</string>\n    <string name=\"play\">Play</string>\n    <string name=\"play_with_selected_questions\">Play with selected questions</string>\n    <string name=\"unselect_all\">Unselect All</string>\n    <string name=\"select_all\">Select All</string>\n    <string name=\"delete_selected\">Delete Selected</string>\n    <string name=\"download_questions\">Download questions</string>\n    <string name=\"sort_by_default\">Sort by Default</string>\n    <string name=\"sort_by_description\">Sort by Description</string>\n    <string name=\"sort_by_category\">Sort by Category</string>\n    <string name=\"country_capital_flags\">Country capital flags</string>\n    <string name=\"comparison_quiz\">Comparison quiz</string>\n    <string name=\"or\">or</string>\n    <string name=\"position_n\">Position: %1$d</string>\n    <string name=\"highest_n\">Highest: %1$d</string>\n    <string name=\"your_score\">Your score</string>\n    <string name=\"highest_score\">Highest score</string>\n    <string name=\"select_a_comparison_mode_for_the_first_item\">Select a comparison mode for the first item</string>\n    <string name=\"image_category_of_s\">Image category of %1$s</string>\n    <string name=\"icon_of_s\">Icon of %1$s</string>\n    <string name=\"greater\">Greater</string>\n    <string name=\"lesser\">Lesser</string>\n    <string name=\"email\">Email</string>\n    <string name=\"password\">Password</string>\n    <string name=\"name\">Name</string>\n    <string name=\"next\">Next</string>\n    <string name=\"random_quiz\">Random Quiz</string>\n    <string name=\"random\">Random</string>\n    <string name=\"quiz_with_random_categories\">Quiz with random categories</string>\n    <string name=\"see_all_categories\">See all categories</string>\n    <string name=\"see_less_categories\">See less categories</string>\n    <string name=\"clear_recent_categories\">Clear recent categories</string>\n    <string name=\"clear_recent_categories_description\">Remove all recent categories from the list screen</string>\n    <string name=\"loading_questions\">Loading questions…</string>\n    <string name=\"quiz_steps_row_container\">Quiz steps container</string>\n    <string name=\"question_n_correct\">Question %1$d - Correct</string>\n    <string name=\"question_n_incorrect\">Question %1$d - Incorrect</string>\n    <!-- Analytics -->\n    <string name=\"analytics\">Analytics</string>\n    <string name=\"analytics_collection_enabled\">Analytics collection enabled</string>\n    <string name=\"data_collection_consent\">Data Collection Consent</string>\n    <string name=\"data_collection_consent_description\">\n        Thank you for using NewQuiz! To help us improve the app and fix any issues that may arise, we collect certain data from your device. The data we collect includes:\\n\\n\n        \\t•   Analytics data: This includes information about how you use the app, such as which features you use most frequently, how long you spend on each page, and which quizzes you complete.\\n\\n\n        \\t•   Crash data: If the app crashes while you\\'re using it, we collect data about the crash to help us identify and fix the issue.\\n\\n\n        \\t•   Performance data: We collect information about how the app performs on your device, such as how quickly pages load and how long it takes to complete quizzes.\\n\\n\n        We take your privacy seriously and only use this data for app improvement purposes. Your data will never be sold or shared with third parties for marketing purposes.\\n\\n\n        By using the NewQuiz, you consent to the collection of this data. If you ever change your mind, you can disable data collection in the app settings.\\n\\n\n        Thank you for your understanding and support!\n    </string>\n    <string name=\"data_collection_consent_agree\">Yes, I consent</string>\n    <string name=\"data_collection_consent_disagree\">No, I do not consent</string>\n    <string name=\"general_analytics_enabled\">General analytics enabled</string>\n    <string name=\"general_analytics_description_enabled\">Enables general analytics such as events, example: game starts, etc.</string>\n    <string name=\"crash_analytics_enabled\">Crash analytics enabled</string>\n    <string name=\"crash_analytics_description_enabled\">Enables crash data analytics such as app crashes and exceptions.</string>\n    <string name=\"performance_monitoring_enabled\">Performance monitoring enabled</string>\n    <string name=\"performance_monitoring_description_enabled\">Enables performance monitoring such as app startup time and other performance metrics.</string>\n    <!-- End Analytics -->\n    <!-- Daily Challenge -->\n    <string name=\"daily_challenge\">Daily Challenge</string>\n    <plurals name=\"play_random_multi_choice_quiz_game\">\n        <item quantity=\"one\">Play one random multi choice quiz game.</item>\n        <item quantity=\"other\">Play %d random multi choice quiz games.</item>\n    </plurals>\n    <plurals name=\"end_multi_choice_quiz_game\">\n        <item quantity=\"one\">End one multi choice quiz game.</item>\n        <item quantity=\"other\">End %d multi choice quiz games.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_questions\">\n        <item quantity=\"one\">Answer one question in multi choice quiz game.</item>\n        <item quantity=\"other\">Answer %d questions in multi choice quiz game.</item>\n    </plurals>\n    <plurals name=\"get_multi_choice_quiz_answer_correct\">\n        <item quantity=\"one\">Get one answer correct in multi choice quiz game.</item>\n        <item quantity=\"other\">Get %d answers correct in multi choice quiz game.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_game_in_category\">\n        <item quantity=\"one\">Play one game with category of %2$s in multi choice quiz.</item>\n        <item quantity=\"other\">Play %1$d games with category of %2$s in multi choice quiz.</item>\n    </plurals>\n    <plurals name=\"get_wordle_word_correct\">\n        <item quantity=\"one\">Get one word correct in wordle game.</item>\n        <item quantity=\"other\">Get %d words correct in wordle game.</item>\n    </plurals>\n    <plurals name=\"play_wordle_game_in_category\">\n        <item quantity=\"one\">Play one game with category of %2$s in wordle game.</item>\n        <item quantity=\"other\">Play %1$d games with category of %2$s in wordle game.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_category\">\n        <item quantity=\"one\">Play one game with category of %2$s in comparison quiz.</item>\n        <item quantity=\"other\">Play %1$d games with category of %2$s in comparison quiz.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_and_get_score\">\n        <item quantity=\"one\">Play one game and get a score of %2$d in comparison quiz.</item>\n        <item quantity=\"other\">Play %1$d games and get a score of %2$d in comparison quiz.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_comparison_mode\">\n        <item quantity=\"one\">Play one game with %2$s mode in comparison quiz.</item>\n        <item quantity=\"other\">Play %1$d games with %2$s mode in comparison quiz.</item>\n    </plurals>\n    <!-- End Daily Challenge -->\n    <string name=\"hide_online_categories\">Hide online categories</string>\n    <string name=\"hide_online_categories_description\">Hide categories that require internet connection, when device is offline.</string>\n    <string name=\"no_categories_available\">No categories available</string>\n\n    <string name=\"target_language\">Target Language</string>\n    <string name=\"target_language_description\">Select the language you want to translate to</string>\n    <string name=\"download_settings\">Download Settings</string>\n    <string name=\"require_wifi\">Require Wi-Fi</string>\n    <string name=\"translation_require_wifi_description\">Only download the translation model when connected to WiFi.</string>\n    <string name=\"require_charging\">Require Charging</string>\n    <string name=\"translation_require_charging_description\">Only download the translation model when the device is charging.</string>\n    <string name=\"confirm\">Confirm</string>\n    <string name=\"others\">Others</string>\n    <string name=\"require_internet_connection\">Requires internet connection</string>\n    <string name=\"dont_require_internet_connection\">Don\\'t require internet connection</string>\n    <string name=\"none\">None</string>\n    <string name=\"both\">Both</string>\n    <string name=\"show_category_connection_info\">Show category connection info</string>\n    <string name=\"xp\">XP</string>\n    <string name=\"total_xp\">Total XP</string>\n    <string name=\"today\">Today</string>\n    <string name=\"this_week\">This Week</string>\n    <string name=\"general_settings_summary\">Categories, Animations</string>\n    <string name=\"multi_choice_settings_summary\">Question count</string>\n    <string name=\"wordle_settings_summary\">Hard mode, Quiz language, Letter hints</string>\n    <string name=\"translation_settings_summary\">Translation language</string>\n    <string name=\"about_and_help_settings_summary\">Version, Contact, Open source licences</string>\n    <string name=\"select_only_offline\">Select only Offline</string>\n    <string name=\"maze_settings_auto_scroll_title\">Auto scroll to current question</string>\n    <string name=\"maze_settings_auto_scroll_summary\">Automatically scroll to current question when playing maze.</string>\n    <string name=\"remaining_time\">Remaining: %1$s</string>\n    <string name=\"claim_n_diamonds\">Claim %1$d \\uD83D\\uDC8E</string>\n    <string name=\"regional_preferences\">Regional Preferences</string>\n    <string name=\"temperature_unit\">Temperature Unit</string>\n    <string name=\"system_default\">System Default</string>\n    <string name=\"celsius\">Celsius</string>\n    <string name=\"fahrenheit\">Fahrenheit</string>\n    <string name=\"distance_unit\">Distance Unit</string>\n    <string name=\"metric\">Metric</string>\n    <string name=\"imperial\">Imperial</string>\n    <string name=\"no_data\">No data</string>\n    <string name=\"no_questions_saved_yet\">No questions saved yet!</string>\n    <string name=\"no_questions_saved_description\">Save or download questions to start playing offline.</string>\n    <string name=\"level_completed\">Level completed!</string>\n    <string name=\"next_level\">Next Level</string>\n    <string name=\"main_menu\">Main Menu</string>\n    <string name=\"try_again\">Try Again</string>\n    <string name=\"level_failed\">Level failed!</string>\n    <string name=\"level_failed_description\">Don\\'t give up! Try again to beat this level.</string>\n    <string name=\"error_verifying_word\">Error verifying word!</string>\n    <string name=\"need_to_use_all_hints_error\">You need to use all hints from last row!</string>\n    <string name=\"empty_word\">Empty word</string>\n    <string name=\"word_not_only_letters_error\">The word must contain only letters</string>\n    <string name=\"word_not_only_digits_error\">The word must contain only digits</string>\n    <string name=\"word_invalid_math_formula_error\">Left formula is not equal to right solution</string>\n    <string name=\"invalid_questions\">Invalid questions</string>\n    <string name=\"invalid_questions_description\">There are %1$d questions with invalid categories. Remove the questions to continue playing or restart the maze.</string>\n    <string name=\"remove_them\">Remove them</string>\n    <string name=\"category_information\">Category information</string>\n    <string name=\"cancel\">Cancel</string>\n    <string name=\"restart\">Restart</string>\n    <string name=\"restart_maze_dialog_description\">Are you sure you want to restart the maze?</string>\n</resources>"
  },
  {
    "path": "core/src/main/res/values/themes.xml",
    "content": "<resources>\n    <!-- Base application theme. -->\n    <style name=\"Base.Theme.NewQuiz\" parent=\"Theme.Material3.DayNight.NoActionBar\"/>\n\n    <style name=\"Theme.NewQuiz\" parent=\"Base.Theme.NewQuiz\" />\n</resources>"
  },
  {
    "path": "core/src/main/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"back\">Zurück</string>\n    <string name=\"more_options\">Mehr Optionen</string>\n    <string name=\"saved_questions\">Gespeicherte Fragen</string>\n    <string name=\"wordle_infinite\">Unendliches Wordle</string>\n    <string name=\"wordle_daily\">Tägliches Wordle</string>\n    <string name=\"verify\">Überprüfen</string>\n    <string name=\"close\">Schließen</string>\n    <string name=\"game_over\">Spiel vorbei</string>\n    <string name=\"item_empty\">Element leer</string>\n    <string name=\"item_i_none\">Kein Element %1$s</string>\n    <string name=\"quick_quiz\">Kurzes Quiz</string>\n    <string name=\"play_again\">Erneut Spielen</string>\n    <string name=\"item_i_present\">Element %1$s vorhanden</string>\n    <string name=\"generate_maze\">Labyrinth generieren</string>\n    <string name=\"generate_offline_maze_description\">Erstelle ein Labyrinth mit allen Spielmodi, die keine Internetverbindung benötigen.</string>\n    <string name=\"random_maze\">Zufälliges Labyrinth</string>\n    <string name=\"generate_maze_with_random_items\">Generiere ein Labyrinth mit zufälligen Fragen, alle Spielmodi sind enthalten.</string>\n    <string name=\"expand_custom_options\">Benutzerdefinierte Optionen anzeigen</string>\n    <string name=\"custom_maze_multi_choice_warning\">Bei Verwendung eines benutzerdefinierten Seeds werden Multi-Choice-Fragen nicht nach dem Zufallsprinzip generiert.</string>\n    <string name=\"generate\">Generieren</string>\n    <string name=\"seed\">Seed</string>\n    <string name=\"number_trivia\">Zahlen-Trivia</string>\n    <string name=\"wordle_animations_enabled\">Wordle Animationen aktiviert</string>\n    <string name=\"wordle_animations_enabled_description\">Aktiviere Animationen für den Wordle-Spielmodus.</string>\n    <string name=\"multi_choice_animations_enabled\">Multi-Choice-Quiz-Animationen aktiviert</string>\n    <string name=\"multi_choice_animations_enabled_description\">Aktiviere Animationen für den Multi-Choice-Quiz-Spielmodus.</string>\n    <string name=\"play\">Spielen</string>\n    <string name=\"play_with_selected_questions\">Mit ausgewählten Fragen spielen</string>\n    <string name=\"unselect_all\">Alle abwählen</string>\n    <string name=\"sort_by_default\">Sortieren nach Standard</string>\n    <string name=\"sort_by_category\">Sortieren nach Kategorie</string>\n    <string name=\"or\">oder</string>\n    <string name=\"highest_n\">Höchstes: %1$d</string>\n    <string name=\"highest_score\">Höchste Punktzahl</string>\n    <string name=\"select_a_comparison_mode_for_the_first_item\">Wählen Sie einen Vergleichsmodus für die erste Position</string>\n    <string name=\"image_category_of_s\">Bildkategorie von %1$s</string>\n    <string name=\"greater\">Größer</string>\n    <string name=\"name\">Name</string>\n    <string name=\"random_quiz\">Zufälliges Quiz</string>\n    <string name=\"random\">Zufall</string>\n    <string name=\"clear_recent_categories\">Letzte Kategorien löschen</string>\n    <string name=\"clear_recent_categories_description\">Alle letzten Kategorien aus dem Listenbildschirm entfernen</string>\n    <string name=\"question_n_correct\">Frage %1$d - Richtig</string>\n    <string name=\"question_n_incorrect\">Frage %1$d - Falsch</string>\n    <string name=\"loading_questions\">Lade Fragen …</string>\n    <string name=\"quiz_steps_row_container\">Quizschritte Container</string>\n    <string name=\"analytics\">Analytik</string>\n    <string name=\"analytics_collection_enabled\">Analytische Erfassung aktiviert</string>\n    <string name=\"data_collection_consent_description\">Danke, dass du NewQuiz benutzt! Um die App zu verbessern und eventuell auftretende Probleme zu beheben, sammeln wir bestimmte Daten von Ihrem Gerät. Die Daten, die wir sammeln, umfassen:\n\\n\n\\n \\t- Analysedaten: Dazu gehören Informationen darüber, wie du die App nutzt, z.B. welche Funktionen du am häufigsten nutzt, wie lange du auf jeder Seite verweilst und welche Quizfragen du abschließt.\n\\n\n\\n \\t- Absturzdaten: Wenn die App abstürzt, während du sie benutzt, sammeln wir Daten über den Absturz, damit wir das Problem identifizieren und beheben können.\n\\n\n\\n \\t- Leistungsdaten: Wir sammeln Informationen darüber, wie die App auf deinem Gerät funktioniert, z. B. wie schnell Seiten geladen werden und wie lange es dauert, Quizfragen zu beantworten.\n\\n\n\\n Wir nehmen Ihre Privatsphäre ernst und verwenden diese Daten nur für die Verbesserung der App. Die Daten werden niemals verkauft oder zu Marketingzwecken an Dritte weitergegeben.\n\\n\n\\n Durch die Nutzung von NewQuiz erklärst du dich mit der Erfassung dieser Daten einverstanden. Solltest du deine Meinung ändern, kannst du die Datenerfassung in den Einstellungen der App deaktivieren.\n\\n\n\\n Vielen Dank für dein Verständnis und deine Unterstützung!</string>\n    <string name=\"data_collection_consent_disagree\">Nein, ich bin nicht einverstanden</string>\n    <string name=\"general_analytics_description_enabled\">Ermöglicht allgemeine Analysen wie Ereignisse, z. B. Spielbeginn usw.</string>\n    <string name=\"crash_analytics_enabled\">Crash-Analyse aktiviert</string>\n    <string name=\"crash_analytics_description_enabled\">Ermöglicht die Analyse von Absturzdaten wie App-Abstürze und Fehlern.</string>\n    <string name=\"performance_monitoring_enabled\">Leistungsüberwachung aktiviert</string>\n    <string name=\"performance_monitoring_description_enabled\">Ermöglicht die Leistungsüberwachung, z. B. die Startzeit der Anwendung und andere Leistungskennzahlen.</string>\n    <string name=\"daily_challenge\">Tägliche Herausforderung</string>\n    <plurals name=\"play_random_multi_choice_quiz_game\">\n        <item quantity=\"one\">Spiele ein zufälliges Multi-Choice-Quizspiel.</item>\n        <item quantity=\"other\">Spiele %d zufällige Multi-Choice-Quizspiele.</item>\n    </plurals>\n    <plurals name=\"end_multi_choice_quiz_game\">\n        <item quantity=\"one\">Beende ein Multi-Choice-Quizspiel.</item>\n        <item quantity=\"other\">Beende %d Multi-Choice-Quizspiele.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_questions\">\n        <item quantity=\"one\">Beantworte eine Frage im Multi-Choice-Quizspiel.</item>\n        <item quantity=\"other\">Beantworte %d Fragen im Multi-Choice-Quizspiel.</item>\n    </plurals>\n    <plurals name=\"get_multi_choice_quiz_answer_correct\">\n        <item quantity=\"one\">Beantworte eine Frage richtig in einem Multi-Choice-Quizspiel.</item>\n        <item quantity=\"other\">Beantworte %d Fragen richtig in einem Multi-Choice-Quizspiel.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_game_in_category\">\n        <item quantity=\"one\">Spiele ein Spiel mit der Kategorie %2$s im Multi-Choice-Quiz.</item>\n        <item quantity=\"other\">Spiele %1$d Spiele mit der Kategorie %2$s im Multi-Choice-Quiz.</item>\n    </plurals>\n    <plurals name=\"get_wordle_word_correct\">\n        <item quantity=\"one\">Finde ein richtiges Wort im Wordle-Spiel.</item>\n        <item quantity=\"other\">Finde %d richtige Wörter im Wordle-Spiel.</item>\n    </plurals>\n    <plurals name=\"play_wordle_game_in_category\">\n        <item quantity=\"one\">Spiele ein Spiel mit der Kategorie %2$s im Wordle-Spiel.</item>\n        <item quantity=\"other\">Spiele %1$d Spiele mit der Kategorie %2$s im Wordle-Spiel.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_category\">\n        <item quantity=\"one\">Spiele ein Spiel mit der Kategorie %2$s im Vergleichsquiz.</item>\n        <item quantity=\"other\">Spiele %1$d Spiele mit der Kategorie %2$s im Vergleichsquiz.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_and_get_score\">\n        <item quantity=\"one\">Spiele ein Spiel und erreiche eine Punktzahl von %2$d im Vergleichsquiz.</item>\n        <item quantity=\"other\">Spiele %1$d Spiele und erreiche eine Punktzahl von %2$d im Vergleichsquiz.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_comparison_mode\">\n        <item quantity=\"one\">Spiele ein Spiel mit dem %2$s-Modus im Vergleichsquiz.</item>\n        <item quantity=\"other\">Spiele %1$d Spiele mit dem %2$s-Modus im Vergleichsquiz.</item>\n    </plurals>\n    <string name=\"hide_online_categories\">Online-Kategorien ausblenden</string>\n    <string name=\"hide_online_categories_description\">Kategorien ausblenden, die eine Internetverbindung erfordern, wenn das Gerät offline ist.</string>\n    <string name=\"item_i_correct\">Element %1$s korrekt</string>\n    <string name=\"four_letters\">4 Buchstaben</string>\n    <string name=\"back_month\">Monat zurück</string>\n    <string name=\"next_month\">Nächster Monat</string>\n    <string name=\"settings\">Einstellungen</string>\n    <string name=\"general\">Allgemein</string>\n    <string name=\"quiz\">Quiz</string>\n    <string name=\"quiz_language\">Quizsprache</string>\n    <string name=\"portuguese\">Portugiesisch</string>\n    <string name=\"color_blind_mode\">Farbenblindheit-Modus</string>\n    <string name=\"high_contrast_colors\">Kontrastreiche Farben</string>\n    <string name=\"info\">Info</string>\n    <string name=\"letter_hints\">Hinweise zu Buchstaben</string>\n    <string name=\"hard_mode\">Schwerer Modus</string>\n    <string name=\"any_revealed_hints_must_be_used_in_subsequest_guesses\">Alle aufgedeckten Hinweise müssen bei folgenden Versuchen verwendet werden</string>\n    <string name=\"clean_calendar_data\">Kalenderdaten löschen</string>\n    <string name=\"wordle_infinite_row_limit_value\">Wordle endlose Versuche Grenze.</string>\n    <string name=\"clear_settings\">Einstellungen zurücksetzten</string>\n    <string name=\"remove_all_saved_settings\">Entfernt alle gespeicherten Einstellungen</string>\n    <string name=\"home\">Startseite</string>\n    <string name=\"today_game\">Heutiges Spiel</string>\n    <string name=\"multi_choice_quiz\">Multi-Choice-Quiz</string>\n    <string name=\"logo_quiz\">Logo-Quiz</string>\n    <string name=\"other\">Andere</string>\n    <string name=\"general_knowledge\">Allgemeines Wissen</string>\n    <string name=\"entertainment_film\">Filme</string>\n    <string name=\"entertainment_music\">Musik</string>\n    <string name=\"entertainment_musicals_and_theatres\">Musicals &amp; Theater</string>\n    <string name=\"entertainment_television\">Fernsehen</string>\n    <string name=\"entertainment_board_games\">Brettspiele</string>\n    <string name=\"science_and_nature\">Wissenschaft &amp; Natur</string>\n    <string name=\"science_computers\">Computer</string>\n    <string name=\"sports\">Sport</string>\n    <string name=\"geography\">Geografie</string>\n    <string name=\"history\">Geschichte</string>\n    <string name=\"politics\">Politik</string>\n    <string name=\"celebrities\">Berühmte Persönlichkeiten</string>\n    <string name=\"animals\">Tiere</string>\n    <string name=\"vehicles\">Fahrzeuge</string>\n    <string name=\"entertainment_comics\">Comics</string>\n    <string name=\"science_gadgets\">Gadgets</string>\n    <string name=\"entertainment_japanese_anime_and_manga\">Japanische Anime &amp; Manga</string>\n    <string name=\"entertainment_cartoon_and_animations\">Zeichentrickfilm &amp; Animationen</string>\n    <string name=\"categories\">Kategorien</string>\n    <plurals name=\"n_questions_available\">\n        <item quantity=\"one\">%1$s Frage vorhanden</item>\n        <item quantity=\"other\">%1$s Fragen vorhanden</item>\n    </plurals>\n    <string name=\"difficulty\">Schwierigkeitsgrad</string>\n    <string name=\"is_not_in_the_target_word_wordle\">\" nicht im Zielwort enthalten ist.\"</string>\n    <string name=\"online\">Online</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"correct_answers\">Richtige Antworten</string>\n    <string name=\"total_questions\">Gesamtzahl der Fragen</string>\n    <string name=\"last_quiz_times\">Letzte Quizzeiten</string>\n    <string name=\"time\">Zeit</string>\n    <string name=\"last_questions\">Letzte Fragen</string>\n    <string name=\"correct_words\">Richtige Wörter</string>\n    <string name=\"total_words\">Wörter insgesamt</string>\n    <string name=\"user_xp\">Anwender Erfahrung</string>\n    <string name=\"level\">Level</string>\n    <string name=\"photo_of_s\">Foto von %1$s</string>\n    <string name=\"good_morning\">Guten Morgen</string>\n    <string name=\"good_afternoon\">Guten nachmittag</string>\n    <string name=\"good_evening\">Guten abend</string>\n    <string name=\"good_night\">Guten nacht</string>\n    <string name=\"no_diamonds\">Keine Diamanten</string>\n    <string name=\"skip_question_q\">Frage überspringen\\?</string>\n    <string name=\"guess_math_formula\">Errate die Matheformel</string>\n    <string name=\"guess_solution\">Lösung erraten</string>\n    <string name=\"math_quiz\">Mathe-Quiz</string>\n    <string name=\"maze\">Labyrinth</string>\n    <string name=\"easy\">Leicht</string>\n    <string name=\"medium\">Medium</string>\n    <string name=\"hard\">Schwer</string>\n    <string name=\"open_drawer\">Seitennavigation öffnen</string>\n    <string name=\"copy_maze_seed\">Labyrinthsamen kopieren</string>\n    <string name=\"restart_maze\">Labyrinth neu starten</string>\n    <string name=\"generate_offline_maze\">Labyrinth offline generieren</string>\n    <string name=\"custom_maze\">Benutzerdefiniertes Labyrinth</string>\n    <string name=\"generate_maze_with_custom_options\">Labyrinth mit benutzerdefinierten Optionen generieren</string>\n    <string name=\"user\">Benutzer</string>\n    <string name=\"about_and_help\">Info und Hilfe</string>\n    <string name=\"animations\">Animationen</string>\n    <string name=\"animations_enabled\">Animationen aktiviert</string>\n    <string name=\"global_animations_enabled\">Globale Animationen aktiviert</string>\n    <string name=\"sort_questions\">Fragen sortieren</string>\n    <string name=\"play_with_random_saved_questions\">Quiz mit zufällig gespeicherten Fragen spielen</string>\n    <string name=\"select_all\">Alle auswählen</string>\n    <string name=\"delete_selected\">Ausgewählte löschen</string>\n    <string name=\"download_questions\">Fragen herunterladen</string>\n    <string name=\"sort_by_description\">Sortieren nach Beschreibung</string>\n    <string name=\"country_capital_flags\">Flaggen der Landeshauptstädte</string>\n    <string name=\"comparison_quiz\">Vergleichsquiz</string>\n    <string name=\"position_n\">Position: %1$d</string>\n    <string name=\"your_score\">Deine Punktzahl</string>\n    <string name=\"icon_of_s\">Symbol von %1$s</string>\n    <string name=\"lesser\">Kleiner</string>\n    <string name=\"email\">E-Mail</string>\n    <string name=\"password\">Passwort</string>\n    <string name=\"next\">Nächstes</string>\n    <string name=\"quiz_with_random_categories\">Quiz mit zufälligen Kategorien</string>\n    <string name=\"see_all_categories\">Alle Kategorien anzeigen</string>\n    <string name=\"see_less_categories\">Weniger Kategorien anzeigen</string>\n    <string name=\"data_collection_consent\">Zustimmung zur Datenerhebung</string>\n    <string name=\"data_collection_consent_agree\">Ja, ich stimme zu</string>\n    <string name=\"general_analytics_enabled\">Allgemeine Analytik aktiviert</string>\n    <string name=\"no_categories_available\">Keine Kategorien verfügbar</string>\n    <string name=\"five_letters\">5 Buchstaben</string>\n    <string name=\"six_letters\">6 Buchstaben</string>\n    <string name=\"english\">Englisch</string>\n    <string name=\"spanish\">Spanisch</string>\n    <string name=\"french\">Französisch</string>\n    <string name=\"hint_above_the_letter_that_it_appears_twice_or_more_in_the_hidden_word\">Hinweis über dem Buchstaben, dass er zweimal oder öfter in dem versteckten Wort vorkommt</string>\n    <string name=\"rows_limited\">Begrenzte Versuche</string>\n    <string name=\"wordle_infinite_row_limited\">Wordle unendliche Versuche begrenzt.</string>\n    <string name=\"clean_saved_calendar_wins_losses\">Löscht alle gespeicherten Kalender Siege/Verluste.</string>\n    <string name=\"today_random_game\">Heutiges zufälliges Spiel</string>\n    <string name=\"flag_quiz\">Flaggenquiz</string>\n    <string name=\"entertainment_books\">Bücher</string>\n    <string name=\"entertainment_video_games\">Videospiele</string>\n    <string name=\"science_mathematics\">Mathematik</string>\n    <string name=\"mythology\">Mythologie</string>\n    <string name=\"art\">Kunst</string>\n    <string name=\"see_all\">Alle anzeigen</string>\n    <string name=\"is_in_the_word_but_in_the_wrong_spot_wordle\">\" im Zielwort enthalten, aber an anderer Stelle.\"</string>\n    <string name=\"is_in_the_word_and_in_the_correct_spot_wordle\">\" steht im Wort und an der richtigen Stelle.\"</string>\n    <string name=\"results_screen\">Ergebnisbildschirm</string>\n    <string name=\"n_correct_questions\">%1$s richtige Fragen</string>\n    <string name=\"translation_enabled\">Übersetzung aktivierte</string>\n    <string name=\"quiz_question_size\">Anzahl der Quizfragen</string>\n    <string name=\"download_translation_model\">Übersetzungsmodell herunterladen</string>\n    <string name=\"translation\">Übersetzung</string>\n    <string name=\"delete_translation_model\">Übersetzungsmodell löschen</string>\n    <string name=\"current_xp\">Derzeitige Erfahrung</string>\n    <string name=\"required_xp_to_next_level\">Erforderliche Erfahrung für die nächste Stufe</string>\n    <plurals name=\"you_have_n_diamonds_skip_question_q\">\n        <item quantity=\"one\">Du hast einen Diamanten. Möchtest du den Diamanten benutzen, um diese Frage zu überspringen\\?</item>\n        <item quantity=\"other\">Du hast %1$d Diamanten, möchtest du %2$d Diamanten verwenden, um diese Frage zu überspringen\\?</item>\n    </plurals>\n    <string name=\"no_diamonds_to_skip_question_description\">Du hast nicht genügend Diamanten, um diese Frage zu überspringen!</string>\n    <string name=\"skip\">Überspringen</string>\n    <string name=\"save\">Speichern</string>\n    <string name=\"dismiss\">Ablehnen</string>\n    <string name=\"guess_the_word\">Errate das Wort</string>\n    <string name=\"guess_the_number\">Errate die Zahl</string>\n</resources>"
  },
  {
    "path": "core/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">NewQuiz</string>\n    <string name=\"quick_quiz\">Cuestionario rápido</string>\n    <string name=\"back\">Atrás</string>\n    <string name=\"more_options\">Mas opciones</string>\n    <string name=\"saved_questions\">Preguntas guardadas</string>\n    <string name=\"wordle\">Wordle</string>\n    <string name=\"wordle_infinite\">Palabra infinita</string>\n    <string name=\"wordle_daily\">Palabra diaria</string>\n    <string name=\"verify\">Verificar</string>\n    <string name=\"play_again\">Juega de nuevo</string>\n    <string name=\"close\">Cerrar</string>\n    <string name=\"game_over\">Juego terminado</string>\n    <string name=\"item_empty\">Letra vacío</string>\n    <string name=\"item_i_none\">Ningún elemento %1$s</string>\n    <string name=\"item_i_present\">Artículo %1$s presente</string>\n    <string name=\"item_i_correct\">Artículo %1$s correcto</string>\n    <string name=\"four_letters\">4 letras</string>\n    <string name=\"five_letters\">5 letras</string>\n    <string name=\"six_letters\">6 letras</string>\n    <string name=\"back_month\">Mes anterior</string>\n    <string name=\"next_month\">Próximo mes</string>\n    <string name=\"settings\">Ajustes</string>\n    <string name=\"general\">General</string>\n    <string name=\"quiz\">Prueba</string>\n    <string name=\"quiz_language\">Lenguaje de prueba</string>\n    <string name=\"english\">inglés</string>\n    <string name=\"portuguese\">portugués</string>\n    <string name=\"color_blind_mode\">Modo daltónico</string>\n    <string name=\"high_contrast_colors\">Colores de alto contraste</string>\n    <string name=\"info\">Información</string>\n    <string name=\"letter_hints\">Sugerencias de letras</string>\n    <string name=\"hint_above_the_letter_that_it_appears_twice_or_more_in_the_hidden_word\">Sugerencia sobre la letra que aparece dos veces o más en la palabra oculta</string>\n    <string name=\"hard_mode\">Modo difícil</string>\n    <string name=\"any_revealed_hints_must_be_used_in_subsequest_guesses\">Cualquier sugerencia revelada debe usarse en las suposiciones posteriores</string>\n    <string name=\"rows_limited\">Filas limitadas</string>\n    <string name=\"wordle_infinite_row_limited\">Wordle fila infinita limitada.</string>\n    <string name=\"wordle_infinite_row_limit_value\">Valor de límite de fila infinita de Wordle.</string>\n    <string name=\"clean_calendar_data\">Limpiar los datos del calendario</string>\n    <string name=\"clean_saved_calendar_wins_losses\">Limpia todas las victorias/derrotas guardadas del calendario.</string>\n    <string name=\"clear_settings\">Borrar preferencias</string>\n    <string name=\"remove_all_saved_settings\">Eliminar todas las configuraciones guardadas</string>\n    <string name=\"home\">Casa</string>\n    <string name=\"today_game\">Juego de hoy</string>\n    <string name=\"today_random_game\">Hoy juego aleatorio</string>\n    <string name=\"multi_choice_quiz\">Cuestionario múltiple</string>\n    <string name=\"flag_quiz\">Cuestionario</string>\n    <string name=\"logo_quiz\">Prueba del logo</string>\n    <string name=\"other\">Otro</string>\n    <string name=\"general_knowledge\">Conocimientos generales</string>\n    <string name=\"entertainment_books\">Libros</string>\n    <string name=\"entertainment_film\">Película</string>\n    <string name=\"entertainment_music\">Música</string>\n    <string name=\"entertainment_musicals_and_theatres\">Musicales y teatro</string>\n    <string name=\"entertainment_television\">Televisión</string>\n    <string name=\"entertainment_video_games\">Videojuegos</string>\n    <string name=\"entertainment_board_games\">Juegos de mesa</string>\n    <string name=\"science_and_nature\">Ciencia y naturaleza</string>\n    <string name=\"science_computers\">Ordenadores</string>\n    <string name=\"science_mathematics\">Matemáticas</string>\n    <string name=\"mythology\">Mitología</string>\n    <string name=\"sports\">Deportes</string>\n    <string name=\"geography\">Geografía</string>\n    <string name=\"history\">Historia</string>\n    <string name=\"politics\">Política</string>\n    <string name=\"art\">Arte</string>\n    <string name=\"celebrities\">Famosos</string>\n    <string name=\"animals\">Animales</string>\n    <string name=\"vehicles\">Coches</string>\n    <string name=\"entertainment_comics\">Cómics</string>\n    <string name=\"science_gadgets\">Dispositivos</string>\n    <string name=\"entertainment_japanese_anime_and_manga\">Anime y manga japoneses</string>\n    <string name=\"entertainment_cartoon_and_animations\">Dibujos animados y animaciones</string>\n    <string name=\"categories\">Categorías</string>\n    <string name=\"see_all\">Ver todo</string>\n    <string name=\"difficulty\">Dificultad</string>\n    <string name=\"is_not_in_the_target_word_wordle\">\" no está en la palabra objetivo.\"</string>\n    <string name=\"is_in_the_word_but_in_the_wrong_spot_wordle\">\" está en la palabra pero en el lugar equivocado.\"</string>\n    <string name=\"is_in_the_word_and_in_the_correct_spot_wordle\">\" está en la palabra y en el lugar correcto.\"</string>\n    <string name=\"results_screen\">Pantalla de resultados</string>\n    <string name=\"n_correct_questions\">%1$s preguntas correctas</string>\n    <string name=\"quiz_question_size\">Tamaño de la pregunta del cuestionario</string>\n    <string name=\"translation\">Traducción</string>\n    <string name=\"translation_enabled\">Traducción habilitada</string>\n    <string name=\"download_translation_model\">Descargar modelo de traducción</string>\n    <string name=\"delete_translation_model\">Eliminar el modelo de traducción</string>\n    <string name=\"online\">Conectado</string>\n    <string name=\"profile\">Perfil</string>\n    <string name=\"correct_answers\">Respuestas correctas</string>\n    <string name=\"total_questions\">Total de preguntas</string>\n    <string name=\"last_quiz_times\">Últimos tiempos de cuestionario</string>\n    <string name=\"time\">Hora</string>\n    <string name=\"last_questions\">Últimos preguntas</string>\n    <string name=\"correct_words\">Palabras correctas</string>\n    <string name=\"total_words\">Palabras totales</string>\n    <string name=\"user_xp\">Usuario XP</string>\n    <string name=\"level\">Nivel</string>\n    <string name=\"current_xp\">Experiencia actual</string>\n    <string name=\"required_xp_to_next_level\">Experiencia necesaria para el siguiente nivel</string>\n    <string name=\"photo_of_s\">Foto de %1$s</string>\n    <string name=\"good_morning\">Buenos días</string>\n    <string name=\"good_afternoon\">Buenas tardes</string>\n    <string name=\"good_evening\">Buenas noches</string>\n    <string name=\"good_night\">Buenas noches</string>\n    <string name=\"no_diamonds\">Sin diamantes</string>\n    <string name=\"skip_question_q\">¿Omitir la pregunta?</string>\n    <string name=\"skip\">Saltar</string>\n    <string name=\"save\">Ahorrar</string>\n    <string name=\"dismiss\">Descartar</string>\n    <string name=\"guess_math_formula\">Adivina la fórmula matemática</string>\n    <string name=\"guess_solution\">Solución a las adivinanzas</string>\n    <string name=\"math_quiz\">Preguntas de matemáticas</string>\n    <string name=\"maze\">Laberinto</string>\n    <string name=\"easy\">Fácil</string>\n    <string name=\"medium\">Medio</string>\n    <string name=\"hard\">Duro</string>\n    <string name=\"open_drawer\">Cajón abierto</string>\n    <string name=\"copy_maze_seed\">Copiar semilla de laberinto</string>\n    <string name=\"restart_maze\">Reiniciar laberinto</string>\n    <string name=\"generate_maze\">Generar laberinto</string>\n    <string name=\"generate_offline_maze\">Generar laberinto sin conexión</string>\n    <string name=\"generate_offline_maze_description\">Crea un laberinto con todos los modos de juego que no requieren conexión a Internet.</string>\n    <string name=\"random_maze\">Laberinto aleatorio</string>\n    <string name=\"generate_maze_with_random_items\">Generar laberinto con preguntas aleatorias, se incluirán todos los modos de juego.</string>\n    <string name=\"custom_maze\">Laberinto personalizado</string>\n    <string name=\"generate_maze_with_custom_options\">Generar laberinto con opciones personalizadas</string>\n    <string name=\"expand_custom_options\">Ampliar opciones personalizadas</string>\n    <string name=\"custom_maze_multi_choice_warning\">Cuando se usa una semilla personalizada, las preguntas de múltiples opciones no serán aleatorias generadas por esta semilla.</string>\n    <string name=\"generate\">Generar</string>\n    <string name=\"seed\">Semilla</string>\n    <string name=\"user\">Usuario</string>\n    <string name=\"spanish\">Español</string>\n    <string name=\"french\">Francés</string>\n    <string name=\"number_trivia\">Curiosidades sobre los números</string>\n    <string name=\"about_and_help\">Acerca de y ayuda</string>\n    <string name=\"animations\">Animaciones</string>\n    <string name=\"animations_enabled\">Animaciones habilitadas</string>\n    <string name=\"global_animations_enabled\">Animaciones globales habilitadas</string>\n    <string name=\"wordle_animations_enabled\">Animaciones de Wordle habilitadas</string>\n    <string name=\"analytics\">Analítica</string>\n    <string name=\"analytics_collection_enabled\">Recopilación de análisis habilitada</string>\n    <string name=\"play\">Jugar</string>\n    <string name=\"play_with_selected_questions\">Jugar con las preguntas seleccionadas</string>\n    <string name=\"unselect_all\">Deseleccionar todo</string>\n    <string name=\"select_all\">Seleccionar todo</string>\n    <string name=\"delete_selected\">Borrar las seleccionadas</string>\n    <string name=\"download_questions\">Descargar las preguntas</string>\n    <string name=\"sort_by_default\">Ordenar por defecto</string>\n    <string name=\"sort_by_description\">Ordenar por descripción</string>\n    <string name=\"sort_by_category\">Ordenar por categorías</string>\n    <string name=\"sort_questions\">Ordenar las preguntas</string>\n    <string name=\"play_with_random_saved_questions\">Juega al cuestionario con preguntas guardadas al azar</string>\n    <string name=\"country_capital_flags\">Banderas de las capitales de los países</string>\n    <string name=\"comparison_quiz\">Cuestionario de comparación</string>\n    <string name=\"position_n\">Posición: %1$d</string>\n    <string name=\"highest_n\">Más alto: %1$d</string>\n    <string name=\"your_score\">Tu puntuación</string>\n    <string name=\"highest_score\">Máxima puntuación</string>\n    <string name=\"select_a_comparison_mode_for_the_first_item\">Seleccione un modo de comparación para el primer elemento</string>\n    <string name=\"image_category_of_s\">Categoría de la imagen de %1$s</string>\n    <string name=\"icon_of_s\">Icono de %1$s</string>\n    <string name=\"greater\">Mayor que</string>\n    <string name=\"lesser\">Menor que</string>\n    <string name=\"or\">o</string>\n    <string name=\"email\">Correo electrónico</string>\n    <string name=\"password\">Contraseña</string>\n    <string name=\"name\">Nombre</string>\n    <string name=\"next\">Siguiente</string>\n    <string name=\"guess_the_word\">Adivina la palabra</string>\n    <string name=\"guess_the_number\">Adivina el número</string>\n    <string name=\"random_quiz\">Cuestionario aleatorio</string>\n    <string name=\"random\">Aleatorio</string>\n    <string name=\"quiz_with_random_categories\">Cuestionario con categorías aleatorias</string>\n    <string name=\"see_all_categories\">Ver todas las categorías</string>\n    <string name=\"see_less_categories\">Ver menos categorías</string>\n    <string name=\"clear_recent_categories\">Borrar las categorías recientes</string>\n    <string name=\"clear_recent_categories_description\">Eliminar todas las categorías recientes de la lista</string>\n    <plurals name=\"n_questions_available\">\n        <item quantity=\"one\">%1$s pregunta disponible</item>\n        <item quantity=\"many\">%1$s preguntas disponibles</item>\n        <item quantity=\"other\">%1$s preguntas disponibles</item>\n    </plurals>\n    <plurals name=\"you_have_n_diamonds_skip_question_q\">\n        <item quantity=\"one\">Tienes un diamante, ¿quieres usar el diamante para saltarte esta pregunta\\?</item>\n        <item quantity=\"many\">Tienes %1$d diamantes, ¿quieres usar %2$d diamantes para saltarte esta pregunta\\?</item>\n        <item quantity=\"other\">Tienes %1$d diamantes, ¿quieres usar %2$d diamantes para saltarte esta pregunta\\?</item>\n    </plurals>\n    <string name=\"data_collection_consent\">Consentimiento para la recogida de datos</string>\n    <string name=\"data_collection_consent_description\">\n        ¡Gracias por utilizar NewQuiz! Para ayudarnos a mejorar la aplicación y solucionar cualquier problema que pueda surgir, recopilamos ciertos datos de tu dispositivo. Los datos que recopilamos incluyen:\\n\\n\n        \\t• Datos analíticos: Esto incluye información sobre cómo utiliza la aplicación, como qué funciones utiliza con más frecuencia, cuánto tiempo pasa en cada página y qué cuestionarios completa.\\n\\n\n        \\t• Datos de bloqueos: Si la aplicación se bloquea mientras la utilizas, recopilamos datos sobre el bloqueo para ayudarnos a identificar y solucionar el problema.\\n\\n\n        \\t• Datos de rendimiento: Recopilamos información sobre el rendimiento de la aplicación en tu dispositivo, como la rapidez con la que se cargan las páginas y el tiempo que se tarda en completar los cuestionarios.\\n\\n\n        Nos tomamos muy en serio tu privacidad y sólo utilizamos estos datos para mejorar la aplicación. Sus datos nunca serán vendidos o compartidos con terceros con fines de marketing.\\n\\n\n        Al utilizar NewQuiz, aceptas la recopilación de estos datos. Si alguna vez cambias de opinión, puedes desactivar la recopilación de datos en la configuración de la aplicación.\\n\\n\n        ¡Gracias por tu comprensión y apoyo!\n    </string>\n    <string name=\"data_collection_consent_agree\">Sí, estoy de acuerdo</string>\n    <string name=\"data_collection_consent_disagree\">No, no estoy de acuerdo</string>\n    <string name=\"general_analytics_enabled\">Análisis generales habilitados</string>\n    <string name=\"general_analytics_description_enabled\">Permite análisis generales, como eventos, por ejemplo: inicio del juego, etc.</string>\n    <string name=\"crash_analytics_enabled\">Análisis de fallos habilitados</string>\n    <string name=\"crash_analytics_description_enabled\">Permite el análisis de datos de fallos, como los fallos y excepciones de la aplicación.</string>\n    <string name=\"performance_monitoring_enabled\">Monitoreo de rendimiento habilitado</string>\n    <string name=\"performance_monitoring_description_enabled\">Permite el monitoreo de rendimiento, como el tiempo de inicio de la aplicación y otras métricas de rendimiento.</string>\n    <string name=\"daily_challenge\">Desafío diario</string>\n    <plurals name=\"play_random_multi_choice_quiz_game\">\n        <item quantity=\"one\">Juega a un juego de preguntas y respuestas aleatorio.</item>\n        <item quantity=\"many\">Juega a %d juegos de preguntas y respuestas aleatorias.</item>\n        <item quantity=\"other\">Juega a %d juegos de preguntas y respuestas aleatorias.</item>\n    </plurals>\n    <plurals name=\"end_multi_choice_quiz_game\">\n        <item quantity=\"one\">Termina un juego de preguntas de opción múltiple.</item>\n        <item quantity=\"many\">Termina %d juegos de preguntas de opciones múltiples.</item>\n        <item quantity=\"other\">Termina %d juegos de preguntas de opciones múltiples.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_questions\">\n        <item quantity=\"one\">Responde una pregunta en un juego de preguntas de opciones múltiples.</item>\n        <item quantity=\"many\">Responde %d preguntas en un juego de preguntas de opciones múltiples.</item>\n        <item quantity=\"other\">Responde %d preguntas en un juego de preguntas de opciones múltiples.</item>\n    </plurals>\n    <plurals name=\"get_wordle_word_correct\">\n        <item quantity=\"one\">Consigue una palabra correcta en el juego de Wordle.</item>\n        <item quantity=\"many\">Consigue %d palabras correctas en el juego de Wordle.</item>\n        <item quantity=\"other\">Consigue %d palabras correctas en el juego de Wordle.</item>\n    </plurals>\n    <plurals name=\"play_wordle_game_in_category\">\n        <item quantity=\"one\">Juega un juego con la categoría de %2$s en Wordle Game.</item>\n        <item quantity=\"many\">Juega %1$d juegos con la categoría de %2$s en Wordle Game.</item>\n        <item quantity=\"other\">Juega %1$d juegos con la categoría de %2$s en Wordle Game.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_and_get_score\">\n        <item quantity=\"one\">Juega una partida y consigue una puntuación de %2$d en la prueba comparativa.</item>\n        <item quantity=\"many\">Juega a %1$d juegos y consigue una puntuación de %2$d en la prueba comparativa.</item>\n        <item quantity=\"other\">Juega a %1$d juegos y consigue una puntuación de %2$d en la prueba comparativa.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_game_in_category\">\n        <item quantity=\"one\">Juega un juego con la categoría de %2$s en un cuestionario de opciones múltiples.</item>\n        <item quantity=\"many\">Juega %1$d juegos con la categoría de %2$s en el cuestionario de opciones múltiples.</item>\n        <item quantity=\"other\">Juega %1$d juegos con la categoría de %2$s en el cuestionario de opciones múltiples.</item>\n    </plurals>\n    <plurals name=\"get_multi_choice_quiz_answer_correct\">\n        <item quantity=\"one\">Obten una respuesta correcta en el juego de preguntas de opción múltiple.</item>\n        <item quantity=\"many\">Obten %d respuestas correctas en el juego de preguntas de opciones múltiples.</item>\n        <item quantity=\"other\">Obten %d respuestas correctas en el juego de preguntas de opciones múltiples.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_category\">\n        <item quantity=\"one\">Juega un juego con la categoría de %2$s en el cuestionario de comparación.</item>\n        <item quantity=\"many\">Juega %1$d juegos con la categoría de %2$s en el cuestionario de comparación.</item>\n        <item quantity=\"other\">Juega %1$d juegos con la categoría de %2$s en el cuestionario de comparación.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_comparison_mode\">\n        <item quantity=\"one\">Juega una partida con el modo %2$s en la prueba comparativa.</item>\n        <item quantity=\"many\">Juega %1$d partidas con el modo %2$s en la prueba comparativa.</item>\n        <item quantity=\"other\">Juega %1$d partidas con el modo %2$s en la prueba comparativa.</item>\n    </plurals>\n    <string name=\"wordle_animations_enabled_description\">Habilita las animaciones para el modo de juego Wordle.</string>\n    <string name=\"multi_choice_animations_enabled\">Animaciones de opción múltiple habilitadas</string>\n    <string name=\"multi_choice_animations_enabled_description\">Habilitar las animaciones para el modo de juego multielección.</string>\n    <string name=\"no_diamonds_to_skip_question_description\">¡No tienes diamantes para saltarte esta pregunta!</string>\n    <string name=\"loading_questions\">Cargando preguntas…</string>\n    <string name=\"quiz_steps_row_container\">Contenedor de pasos de Quiz</string>\n    <string name=\"question_n_correct\">Pregunta %1$d - Correcta</string>\n    <string name=\"question_n_incorrect\">Pregunta %1$d - Incorrecta</string>\n    <string name=\"hide_online_categories\">Ocultar las categorías online</string>\n    <string name=\"hide_online_categories_description\">Ocultar las categorías que requieren conexión a Internet, cuando el dispositivo está sin conexión.</string>\n    <string name=\"no_categories_available\">No hay categorías disponibles</string>\n    <string name=\"downloading_translation_model_description\">Descargando el modelo de la traducción, esto puede tomar un tiempo.</string>\n    <string name=\"target_language_description\">Selecciona el idioma al que deseas traducir</string>\n    <string name=\"require_wifi\">Necesita una red WiFi</string>\n    <string name=\"translation_require_charging_description\">Descarga el modelo de la traducción sólo cuando el dispositivo se está cargando.</string>\n    <string name=\"download_settings\">Descargar los Ajustes</string>\n    <string name=\"translation_require_wifi_description\">Descarga el modelo de la traducción únicamente cuando estés conectado a una red WiFi.</string>\n    <string name=\"confirm\">Confirmar</string>\n    <string name=\"require_charging\">Carga obligatoria</string>\n    <string name=\"download_translation_model_description\">Descargar el modelo de la traducción para la traducción en el dispositivo.</string>\n    <string name=\"downloading_translation_model\">Descargando el modelo de la traducción</string>\n    <string name=\"target_language\">Idioma del destino</string>\n    <string name=\"others\">Otros</string>\n    <string name=\"require_internet_connection\">Requiere conexión a Internet</string>\n    <string name=\"dont_require_internet_connection\">No requiere conexión a Internet</string>\n    <string name=\"none\">Ninguno</string>\n    <string name=\"both\">Ambos</string>\n    <string name=\"show_category_connection_info\">Mostrar información de conexión de categoría</string>\n    <string name=\"xp\">XP</string>\n    <string name=\"total_xp\">XP total</string>\n    <string name=\"this_week\">Esta semana</string>\n    <string name=\"today\">Hoy</string>\n    <string name=\"general_settings_summary\">Categorías, Animaciones</string>\n    <string name=\"translation_settings_summary\">Idioma de la traducción</string>\n    <string name=\"wordle_settings_summary\">Modo difícil, lenguaje de prueba, sugerencias de letras</string>\n    <string name=\"about_and_help_settings_summary\">Versión, Contacto, Licencias de código abierto</string>\n    <string name=\"multi_choice_settings_summary\">Recuento de preguntas</string>\n    <string name=\"select_only_offline\">Seleccione solo sin conexión</string>\n    <string name=\"maze_settings_auto_scroll_title\">Desplazamiento automático a la pregunta actual</string>\n    <string name=\"maze_settings_auto_scroll_summary\">Desplazarse automáticamente a la pregunta actual al jugar al laberinto.</string>\n    <string name=\"remaining_time\">Queda: %1$s</string>\n    <string name=\"claim_n_diamonds\">Reclamar %1$d 💎</string>\n    <string name=\"system_default\">Predeterminado del sistema</string>\n    <string name=\"celsius\">Celsius</string>\n    <string name=\"distance_unit\">Unidades de distancia</string>\n    <string name=\"metric\">Métrico</string>\n    <string name=\"regional_preferences\">Preferencias regionales</string>\n    <string name=\"temperature_unit\">Unidad de temperatura</string>\n    <string name=\"imperial\">Imperial</string>\n    <string name=\"fahrenheit\">Fahrenheit</string>\n    <string name=\"no_data\">Sin datos</string>\n    <string name=\"no_questions_saved_yet\">¡Aún no hay preguntas guardadas!</string>\n    <string name=\"no_questions_saved_description\">Guarde o descargue preguntas para comenzar a jugar sin conexión.</string>\n    <string name=\"maze_completed\">¡Laberinto completado!</string>\n    <string name=\"try_again\">Inténtalo de nuevo</string>\n    <string name=\"level_failed\">¡Nivel fallido!</string>\n    <string name=\"level_failed_description\">¡No te rindas! Vuelve a intentarlo para superar este nivel.</string>\n    <string name=\"main_menu\">Menú principal</string>\n    <string name=\"level_completed\">¡Nivel completado!</string>\n    <string name=\"next_level\">Siguiente nivel</string>\n    <string name=\"need_to_use_all_hints_error\">¡necesita utilizar todas las pistas de la última fila!</string>\n    <string name=\"empty_word\">palabra vacía</string>\n    <string name=\"error_verifying_word\">¡Error al verificar la palabra!</string>\n    <string name=\"word_invalid_math_formula_error\">La fórmula de la izquierda no es igual a la solución de la derecha</string>\n    <string name=\"word_not_only_digits_error\">La palabra debe contener solo dígitos</string>\n    <string name=\"word_not_only_letters_error\">La palabra debe contener solo letras</string>\n</resources>"
  },
  {
    "path": "core/src/main/res/values-et/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"saved_questions\">Salvestatud küsimused</string>\n    <string name=\"more_options\">Lisavalikud</string>\n    <string name=\"quick_quiz\">Kiire mälumäng</string>\n    <string name=\"back\">Tagasi</string>\n    <string name=\"wordle_infinite\">Lõputu Wordle</string>\n    <string name=\"wordle_daily\">Igapäevane Wordle</string>\n    <string name=\"verify\">Kontrolli</string>\n    <string name=\"play_again\">Mängi uuesti</string>\n    <string name=\"close\">Sulge</string>\n    <string name=\"game_over\">Mäng on läbi</string>\n</resources>"
  },
  {
    "path": "core/src/main/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">NewQuiz</string>\n    <string name=\"quick_quiz\">Questionnaire rapide</string>\n    <string name=\"back\">Arrière</string>\n    <string name=\"more_options\">Plus d\\'options</string>\n    <string name=\"saved_questions\">Des questions enregistrées</string>\n    <string name=\"wordle\">Bordel</string>\n    <string name=\"wordle_infinite\">Bordle infini</string>\n    <string name=\"wordle_daily\">Bordle quotidien</string>\n    <string name=\"verify\">Vérifier</string>\n    <string name=\"play_again\">Rejouer</string>\n    <string name=\"close\">proche</string>\n    <string name=\"game_over\">Jeu terminé</string>\n    <string name=\"item_empty\">Letter vide</string>\n    <string name=\"item_i_none\">Letter %1$s Aucun</string>\n    <string name=\"item_i_present\">Letter %1$s présent</string>\n    <string name=\"item_i_correct\">Letter %1$s correct</string>\n    <string name=\"four_letters\">4 lettres</string>\n    <string name=\"five_letters\">5 lettres</string>\n    <string name=\"six_letters\">6 lettres</string>\n    <string name=\"back_month\">Mois du dos</string>\n    <string name=\"next_month\">Le mois prochain</string>\n    <string name=\"settings\">Réglages</string>\n    <string name=\"general\">Général</string>\n    <string name=\"quiz\">Questionnaire</string>\n    <string name=\"quiz_language\">Quiz</string>\n    <string name=\"english\">Anglais</string>\n    <string name=\"portuguese\">Portugais</string>\n    <string name=\"color_blind_mode\">Mode aveugle de couleur</string>\n    <string name=\"high_contrast_colors\" >Couleurs de contraste élevé</string>\n    <string name=\"info\">Info</string>\n    <string name=\"letter_hints\">Indices de lettres</string>\n    <string name=\"hint_above_the_letter_that_it_appears_twice_or_more_in_the_hidden_word\">Astuce au-dessus de la lettre qu\\'il apparaît deux fois ou plus dans le mot caché</string>\n    <string name=\"hard_mode\">Mode difficile</string>\n    <string name=\"any_revealed_hints_must_be_used_in_subsequest_guesses\">Tout indice révélé doit être utilisé dans les suppositions suivantes</string>\n    <string name=\"rows_limited\">Lignes limitées</string>\n    <string name=\"wordle_infinite_row_limited\">Wordle Infinite Row Limited.</string>\n    <string name=\"wordle_infinite_row_limit_value\">Valeur limite de ligne infinie de losts.</string>\n    <string name=\"clean_calendar_data\">Données de calendrier propre</string>\n    <string name=\"clean_saved_calendar_wins_losses\">Nettoyez toutes les victoires / pertes du calendrier économisé.</string>\n    <string name=\"clear_settings\">Préférences claires</string>\n    <string name=\"remove_all_saved_settings\">Supprimer tous les paramètres enregistrés</string>\n    <string name=\"home\">Domicile</string>\n    <string name=\"today_game\">Jeu aujourd\\'hui</string>\n    <string name=\"today_random_game\">Aujourd\\'hui Game aléatoire</string>\n    <string name=\"multi_choice_quiz\">Quiz multi-choix</string>\n    <string name=\"flag_quiz\">Quiz de drapeau</string>\n    <string name=\"logo_quiz\">Quiz logo</string>\n    <string name=\"other\">Autre</string>\n    <string name=\"general_knowledge\">Culture générale</string>\n    <string name=\"entertainment_books\">Livres</string>\n    <string name=\"entertainment_film\">Film</string>\n    <string name=\"entertainment_music\">La musique</string>\n    <string name=\"entertainment_musicals_and_theatres\">Musicaux &amp; Théâtres</string>\n    <string name=\"entertainment_television\">Télévision</string>\n    <string name=\"entertainment_video_games\">Jeux vidéo</string>\n    <string name=\"entertainment_board_games\">Jeux de société</string>\n    <string name=\"science_and_nature\">Science &amp; La nature</string>\n    <string name=\"science_computers\">Des ordinateurs</string>\n    <string name=\"science_mathematics\">Mathématiques</string>\n    <string name=\"mythology\">Mythologie</string>\n    <string name=\"sports\">Des sports</string>\n    <string name=\"geography\">Géographie</string>\n    <string name=\"history\">L\\'histoire</string>\n    <string name=\"politics\">Politique</string>\n    <string name=\"art\">Art</string>\n    <string name=\"celebrities\">Célébrités</string>\n    <string name=\"animals\">Animaux</string>\n    <string name=\"vehicles\">Véhicules</string>\n    <string name=\"entertainment_comics\">Des bandes dessinées</string>\n    <string name=\"science_gadgets\">Gadgets</string>\n    <string name=\"entertainment_japanese_anime_and_manga\">Anime japonais &amp; Manga</string>\n    <string name=\"entertainment_cartoon_and_animations\">Dessin animé &amp; Animations</string>\n    <string name=\"categories\">Catégories</string>\n    <string name=\"see_all\">Voir tout</string>\n    <string name=\"difficulty\">Difficulté</string>\n    <string name=\"is_not_in_the_target_word_wordle\">\"n'est pas dans le mot cible.\"</string>\n    <string name=\"is_in_the_word_but_in_the_wrong_spot_wordle\">\"est dans le mot mais au mauvais endroit.\"</string>\n    <string name=\"is_in_the_word_and_in_the_correct_spot_wordle\">\"est dans le mot et au bon endroit.\"</string>\n    <string name=\"results_screen\">Écran des résultats</string>\n    <string name=\"n_correct_questions\">%1$s des questions correctes</string>\n    <string name=\"quiz_question_size\">Taille de la question du quiz</string>\n    <string name=\"translation\">Traduction</string>\n    <string name=\"translation_enabled\">Traduction activée</string>\n    <string name=\"download_translation_model\">Télécharger le modèle de traduction</string>\n    <string name=\"delete_translation_model\">Supprimer le modèle de traduction</string>\n    <string name=\"online\">En ligne</string>\n    <string name=\"profile\">Le profil</string>\n    <string name=\"correct_answers\">Réponses correctes</string>\n    <string name=\"total_questions\">Questions totales</string>\n    <string name=\"last_quiz_times\">Dernières heures de quiz</string>\n    <string name=\"time\">Temps</string>\n    <string name=\"last_questions\">Dernières questions</string>\n    <string name=\"correct_words\">Mots corrects</string>\n    <string name=\"total_words\">Total des mots</string>\n    <string name=\"user_xp\">Utilisateur xp</string>\n    <string name=\"level\">Niveau</string>\n    <string name=\"current_xp\">XP actuel</string>\n    <string name=\"required_xp_to_next_level\">XP requis au niveau supérieur</string>\n    <string name=\"photo_of_s\">Photo de %1$s</string>\n    <string name=\"good_morning\">Bonjour</string>\n    <string name=\"good_afternoon\">Bon après-midi</string>\n    <string name=\"good_evening\">Bonne soirée</string>\n    <string name=\"good_night\">Bonne nuit</string>\n    <string name=\"no_diamonds\">Pas de diamants</string>\n    <string name=\"skip_question_q\">Sauter la question?</string>\n    <string name=\"skip\">Sauter</string>\n    <string name=\"save\">Sauver</string>\n    <string name=\"dismiss\">Rejeter</string>\n    <string name=\"guess_math_formula\">Devinez la formule mathématique</string>\n    <string name=\"guess_solution\">Deviner la solution</string>\n    <string name=\"math_quiz\">Quiz en mathématiques</string>\n    <string name=\"maze\">Labyrinthe</string>\n    <string name=\"easy\">Facile</string>\n    <string name=\"medium\">Moyen</string>\n    <string name=\"hard\">Difficile</string>\n    <string name=\"open_drawer\">Tiroir ouvert</string>\n    <string name=\"copy_maze_seed\">Copier la graine de labyrinthe</string>\n    <string name=\"restart_maze\">Maze de redémarrage</string>\n    <string name=\"generate_maze\">Générer un labyrinthe</string>\n    <string name=\"generate_offline_maze\">Générer un labyrinthe hors ligne</string>\n    <string name=\"generate_offline_maze_description\">Générez le labyrinthe avec tous les modes n\\'a pas besoin de connexion Internet.</string>\n    <string name=\"random_maze\">Labyrinthe aléatoire</string>\n    <string name=\"generate_maze_with_random_items\">Générer le labyrinthe avec des questions aléatoires, tous les modes de jeu seront inclus.</string>\n    <string name=\"custom_maze\">Labyrinthe personnalisé</string>\n    <string name=\"generate_maze_with_custom_options\">Générer le labyrinthe avec des options personnalisées</string>\n    <string name=\"expand_custom_options\">Développer les options personnalisées</string>\n    <string name=\"custom_maze_multi_choice_warning\">Lors de l\\'utilisation de semences personnalisées, les questions multi-choix ne seront pas générées aléatoires par cette graine.</string>\n    <string name=\"generate\">Générer</string>\n    <string name=\"seed\">Graine</string>\n    <string name=\"user\">Utilisateur</string>\n    <string name=\"spanish\">Espagnol</string>\n    <string name=\"french\">Français</string>\n    <string name=\"number_trivia\">Quiz sur les nombres</string>\n    <string name=\"about_and_help\">À propos et aide</string>\n    <string name=\"animations\">Animations</string>\n    <string name=\"animations_enabled\">Animations activées</string>\n    <string name=\"global_animations_enabled\">Animations globales activées</string>\n    <string name=\"wordle_animations_enabled\">Animations Wordle activées</string>\n    <string name=\"analytics\">Analytique</string>\n    <string name=\"analytics_collection_enabled\">Collecte d\\'analyses activée</string>\n</resources>\n"
  },
  {
    "path": "core/src/main/res/values-nb-rNO/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"quick_quiz\">Hurtigquiz</string>\n    <string name=\"back\">Tilbake</string>\n    <string name=\"more_options\">Flere alternativer</string>\n    <string name=\"wordle\">Wordle</string>\n    <string name=\"verify\">Bekreft</string>\n    <string name=\"play_again\">Spill igjen</string>\n    <string name=\"item_empty\">Tomt element</string>\n    <string name=\"four_letters\">4 bokstaver</string>\n    <string name=\"quiz_language\">Quiz-språk</string>\n    <string name=\"spanish\">Spansk</string>\n    <string name=\"french\">Fransk</string>\n    <string name=\"color_blind_mode\">Fargeblindt modus</string>\n    <string name=\"hard_mode\">Vanskelig modus</string>\n    <string name=\"any_revealed_hints_must_be_used_in_subsequest_guesses\">Alle avslørte hint må brukes i påfølgende gjettinger.</string>\n    <string name=\"logo_quiz\">Logo-quiz</string>\n    <string name=\"other\">Annet</string>\n    <string name=\"entertainment_books\">Bøker</string>\n    <string name=\"entertainment_film\">Film</string>\n    <string name=\"entertainment_musicals_and_theatres\">Musikaler og teater</string>\n    <string name=\"clear_settings\">Tøm innstillinger</string>\n    <string name=\"remove_all_saved_settings\">Tøm alle lagrede innstillinger</string>\n    <string name=\"multi_choice_quiz\">Flervalgsquiz</string>\n    <string name=\"flag_quiz\">Flaggquiz</string>\n    <string name=\"home\">Hjem</string>\n    <string name=\"general_knowledge\">Almenkunnskap</string>\n    <string name=\"science_computers\">Datamaskiner</string>\n    <string name=\"sports\">Sport</string>\n    <string name=\"geography\">Geografi</string>\n    <string name=\"politics\">Politikk</string>\n    <string name=\"art\">Kunst</string>\n    <string name=\"animals\">Dyr</string>\n    <string name=\"categories\">Kategorier</string>\n    <string name=\"see_all\">Vis alle</string>\n    <string name=\"difficulty\">Vanskelighetsgrad</string>\n    <string name=\"vehicles\">Kjøretøy</string>\n    <string name=\"is_in_the_word_and_in_the_correct_spot_wordle\">\" er i ordet og på riktig plass.\"</string>\n    <string name=\"delete_translation_model\">Slett oversettelsesmodell</string>\n    <string name=\"total_questions\">Totalt antall spørsmål</string>\n    <string name=\"time\">Tid</string>\n    <string name=\"good_morning\">God morgen</string>\n    <string name=\"good_afternoon\">God ettermiddag</string>\n    <string name=\"good_evening\">God kveld</string>\n    <string name=\"good_night\">God natt</string>\n    <string name=\"no_diamonds\">Ingen diamanter</string>\n    <string name=\"skip_question_q\">Hopp over spørsmål\\?</string>\n    <string name=\"skip\">Hopp over</string>\n    <string name=\"save\">Lagre</string>\n    <string name=\"easy\">Lett</string>\n    <string name=\"open_drawer\">Åpne skuff</string>\n    <string name=\"guess_math_formula\">Gjett matteformel</string>\n    <string name=\"guess_solution\">Gjett løsning</string>\n    <string name=\"maze\">Labyrint</string>\n    <string name=\"random_maze\">Tilfeldig labyrint</string>\n    <string name=\"custom_maze\">Egendefinert labyrint</string>\n    <string name=\"animations\">Animasjoner</string>\n    <string name=\"about_and_help\">«Om» og hjelp</string>\n    <string name=\"app_name\">NewQuiz</string>\n    <string name=\"wordle_daily\">Daglig wordle</string>\n    <string name=\"close\">Lukk</string>\n    <string name=\"game_over\">Spill over</string>\n    <string name=\"wordle_infinite\">Uendelig wordle</string>\n    <string name=\"five_letters\">5 bokstaver</string>\n    <string name=\"item_i_none\">Element %1$s tomt</string>\n    <string name=\"six_letters\">6 bokstaver</string>\n    <string name=\"next_month\">Neste måned</string>\n    <string name=\"general\">Generelt</string>\n    <string name=\"quiz\">Quiz</string>\n    <string name=\"high_contrast_colors\">Høykontrastfarger</string>\n    <string name=\"info\">Info</string>\n    <string name=\"today_game\">Dagens spill</string>\n    <string name=\"entertainment_music\">Musikk</string>\n    <string name=\"entertainment_television\">TV</string>\n    <string name=\"is_in_the_word_but_in_the_wrong_spot_wordle\">\" er i ordet, men på feil plass.\"</string>\n    <string name=\"rows_limited\">Begrensede rader</string>\n    <string name=\"science_mathematics\">Matematikk</string>\n    <string name=\"translation\">Oversettelse</string>\n    <string name=\"correct_answers\">Riktige svar</string>\n    <string name=\"clean_calendar_data\">Tøm kalenderdata</string>\n    <string name=\"medium\">Middels</string>\n    <string name=\"saved_questions\">Lagrede spørsmål</string>\n    <string name=\"item_i_present\">Element %1$s finnes</string>\n    <string name=\"item_i_correct\">Element %1$s er riktig</string>\n    <string name=\"back_month\">Forrige måned</string>\n    <string name=\"portuguese\">Portugisisk</string>\n    <string name=\"entertainment_video_games\">Videospill</string>\n    <string name=\"entertainment_board_games\">Brettspill</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"settings\">Innstillinger</string>\n    <string name=\"english\">Engelsk</string>\n    <string name=\"letter_hints\">Bokstavhint</string>\n    <string name=\"today_random_game\">Dagens tilfeldige spill</string>\n    <string name=\"mythology\">Mytologi</string>\n    <string name=\"history\">Historikk</string>\n    <string name=\"level\">Nivå</string>\n    <string name=\"photo_of_s\">Bilde av %1$s</string>\n    <string name=\"hard\">Vanskelig</string>\n    <string name=\"generate\">Generer</string>\n    <string name=\"user\">Bruker</string>\n    <string name=\"n_correct_questions\">%1$s riktige spørsmål</string>\n    <string name=\"correct_words\">Riktige ord</string>\n    <string name=\"hint_above_the_letter_that_it_appears_twice_or_more_in_the_hidden_word\">Hint over bokstaven om at den finnes to eller flere ganger i det skjulte ordet</string>\n    <string name=\"wordle_infinite_row_limited\">Begrensede rader for uendelig wordle.</string>\n    <string name=\"clean_saved_calendar_wins_losses\">Tømmer alle lagrede kalenderseire/tap.</string>\n    <string name=\"wordle_infinite_row_limit_value\">Radgrenseverdi for uendelig wordle.</string>\n    <string name=\"science_and_nature\">Vitenskap og natur</string>\n    <string name=\"celebrities\">Kjendiser</string>\n    <string name=\"entertainment_comics\">Tegneserier</string>\n    <string name=\"science_gadgets\">Duppeditter</string>\n    <string name=\"entertainment_japanese_anime_and_manga\">Japansk anime og manga</string>\n    <string name=\"entertainment_cartoon_and_animations\">Tegnefilmer og animasjoner</string>\n    <string name=\"is_not_in_the_target_word_wordle\">\" er ikke i målordet.\"</string>\n    <string name=\"results_screen\">Resultatskjerm</string>\n    <string name=\"translation_enabled\">Oversettelser påskrudd</string>\n    <string name=\"download_translation_model\">Last ned oversettelsesmodell</string>\n    <string name=\"quiz_question_size\">Størrelse på quiz-spørsmål</string>\n    <string name=\"online\">Nettbasert</string>\n    <string name=\"last_questions\">Siste spørsmål</string>\n    <string name=\"total_words\">Totalt antall ord</string>\n    <string name=\"user_xp\">Brukererfaring</string>\n    <string name=\"current_xp\">Nåværende erfaring</string>\n    <string name=\"required_xp_to_next_level\">Påkrevd erfaring til neste nivå</string>\n    <string name=\"dismiss\">Forkast</string>\n    <string name=\"math_quiz\">Matte-quiz</string>\n    <string name=\"restart_maze\">Start labyrint på ny</string>\n    <string name=\"generate_maze\">Generer labyrint</string>\n    <string name=\"generate_offline_maze\">Generer frakoblet labyrint</string>\n    <string name=\"generate_offline_maze_description\">Generer labyrint med alle modus krever ikke tilkobling til Internett.</string>\n    <string name=\"copy_maze_seed\">Kopier labyrint-startalgoritme</string>\n    <string name=\"generate_maze_with_random_items\">Generer labyrint med tilfeldige spørsmål. Alle spillmodus vil bli inkludert.</string>\n    <string name=\"generate_maze_with_custom_options\">Generer labyrint med egentilpassede alternativer</string>\n    <string name=\"expand_custom_options\">Utvid egentilpassede alternativer</string>\n    <string name=\"custom_maze_multi_choice_warning\">Når man bruker egendefinert startalgoritme vil flervalgsspørsmål ikke bli generert tilfeldig av denne.</string>\n    <string name=\"seed\">Startalgoritme</string>\n    <string name=\"number_trivia\">Tall-trivia</string>\n    <string name=\"animations_enabled\">Animasjoner påskrudd</string>\n    <string name=\"analytics\">Analyse</string>\n    <string name=\"analytics_collection_enabled\">Innsamling av analysedata påskrudd</string>\n    <string name=\"last_quiz_times\">Siste quiz-tider</string>\n    <string name=\"wordle_animations_enabled\">Wordle-animasjoner påskrudd</string>\n    <string name=\"global_animations_enabled\">Animasjoner i hele programmet påskrudd</string>\n</resources>"
  },
  {
    "path": "core/src/main/res/values-night/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <style name=\"Base.Theme.NewQuiz\" parent=\"Theme.Material3.DayNight.NoActionBar\"/>\n</resources>"
  },
  {
    "path": "core/src/main/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <string name=\"app_name\">NewQuiz</string>\n    <string name=\"quick_quiz\">Quiz rápido</string>\n    <string name=\"back\">Voltar</string>\n    <string name=\"saved_questions\">Perguntas guardadas</string>\n    <string name=\"wordle\">Wordle</string>\n    <string name=\"wordle_infinite\">Wordle infinito</string>\n    <string name=\"wordle_daily\">Wordle diário</string>\n    <string name=\"verify\">Verificar</string>\n    <string name=\"play_again\">Jogar novamente</string>\n    <string name=\"close\">Fechar</string>\n    <string name=\"game_over\">Fim de jogo</string>\n    <string name=\"item_empty\">Item vazio</string>\n    <string name=\"item_i_none\">Item %1$s nada</string>\n    <string name=\"item_i_present\">Item %1$s presente</string>\n    <string name=\"item_i_correct\">Item %1$s correto</string>\n    <string name=\"four_letters\">4 Letras</string>\n    <string name=\"five_letters\">5 Letras</string>\n    <string name=\"six_letters\">6 Letras</string>\n    <string name=\"back_month\">Voltar o mês</string>\n    <string name=\"next_month\">Próximo mês</string>\n    <string name=\"settings\">Opções</string>\n    <string name=\"general\">Geral</string>\n    <string name=\"quiz\">Quiz</string>\n    <string name=\"quiz_language\">Idioma do quiz</string>\n    <string name=\"english\">Inglês</string>\n    <string name=\"portuguese\">Português</string>\n    <string name=\"color_blind_mode\">Modo daltónico</string>\n    <string name=\"high_contrast_colors\">Cores com alto contraste</string>\n    <string name=\"info\">Informações</string>\n    <string name=\"letter_hints\">Ajudas da letra</string>\n    <string name=\"hint_above_the_letter_that_it_appears_twice_or_more_in_the_hidden_word\">Dica acima da letra que aparece duas ou mais na palavra</string>\n    <string name=\"hard_mode\">Modo difícil</string>\n    <string name=\"any_revealed_hints_must_be_used_in_subsequest_guesses\">Todas as dicas usadas têm de ser usadas na próxima linha</string>\n    <string name=\"wordle_infinite_row_limited\">Linhas limitadas no wordle infinito.</string>\n    <string name=\"wordle_infinite_row_limit_value\">Limite das linhas do wordle infinito.</string>\n    <string name=\"clean_calendar_data\">Limpar os dados do calendário</string>\n    <string name=\"clean_saved_calendar_wins_losses\">Limpar todas vitórias/derrotas do calendário.</string>\n    <string name=\"rows_limited\">Linhas limitadas</string>\n    <string name=\"clear_settings\">Apagar Definições</string>\n    <string name=\"remove_all_saved_settings\">Remova todas as definições guardadas</string>\n    <string name=\"home\">Início</string>\n    <string name=\"today_game\">Jogo de hoje</string>\n    <string name=\"today_random_game\">Jogo aleatório de hoje</string>\n    <string name=\"multi_choice_quiz\">Quiz de escolha multipla</string>\n    <string name=\"flag_quiz\">Jogo de bandeiras</string>\n    <string name=\"logo_quiz\">Jogo de logo</string>\n    <string name=\"other\">Outro</string>\n    <string name=\"general_knowledge\">Conhecimento geral</string>\n    <string name=\"entertainment_books\">Livros</string>\n    <string name=\"entertainment_film\">Filmes</string>\n    <string name=\"entertainment_music\">Música</string>\n    <string name=\"entertainment_musicals_and_theatres\">Musicais e teatros</string>\n    <string name=\"entertainment_television\">Televisão</string>\n    <string name=\"entertainment_video_games\">Vídeo jogos</string>\n    <string name=\"entertainment_board_games\">Jogos de tabuleiro</string>\n    <string name=\"science_and_nature\">Ciência e natureza</string>\n    <string name=\"science_computers\">Computadores</string>\n    <string name=\"science_mathematics\">Matemática</string>\n    <string name=\"mythology\">Mitologia</string>\n    <string name=\"sports\">Desportos</string>\n    <string name=\"geography\">Geografia</string>\n    <string name=\"history\">História</string>\n    <string name=\"politics\">Política</string>\n    <string name=\"art\">Arte</string>\n    <string name=\"celebrities\">Celebridades</string>\n    <string name=\"animals\">Animais</string>\n    <string name=\"vehicles\">Veículos</string>\n    <string name=\"entertainment_comics\">Comédia</string>\n    <string name=\"science_gadgets\">Dispositivos</string>\n    <string name=\"entertainment_japanese_anime_and_manga\">Animação japonesa e Manga</string>\n    <string name=\"entertainment_cartoon_and_animations\">Cartoon e animações</string>\n    <string name=\"categories\">Categorias</string>\n    <plurals name=\"n_questions_available\" tools:ignore=\"MissingQuantity,UnusedQuantity\">\n        <item quantity=\"one\">%1$s pergunta disponível</item>\n        <item quantity=\"many\">%1$s perguntas disponíveis</item>\n        <item quantity=\"other\">%1$s perguntas disponíveis</item>\n    </plurals>\n    <string name=\"see_all\">Ver tudo</string>\n    <string name=\"guess_the_word\">Adivinha a Palavra</string>\n    <string name=\"guess_math_formula\">Adivinha a fórmula matematica</string>\n    <string name=\"guess_solution\">Adinha a solução</string>\n    <string name=\"math_quiz\">Jogo de matemática</string>\n    <string name=\"maze\">Labirinto</string>\n    <string name=\"easy\">Fácil</string>\n    <string name=\"medium\">Médio</string>\n    <string name=\"hard\">Difícil</string>\n    <string name=\"difficulty\">Dificuldade</string>\n    <string name=\"is_not_in_the_target_word_wordle\">\" não está na palavra.\"</string>\n    <string name=\"is_in_the_word_but_in_the_wrong_spot_wordle\">\" está na palavra, mas no lugar errado.\"</string>\n    <string name=\"is_in_the_word_and_in_the_correct_spot_wordle\">\" está na palavra e no lugar correto.\"</string>\n    <string name=\"results_screen\">Tela de resultados</string>\n    <string name=\"n_correct_questions\">%1$s perguntas corretas</string>\n    <string name=\"quiz_question_size\">Quantidade de perguntas do quiz</string>\n    <string name=\"translation\">Tradução</string>\n    <string name=\"translation_enabled\">Tradução habilitada</string>\n    <string name=\"download_translation_model\">Transferir o modelo da tradução</string>\n    <string name=\"download_translation_model_description\">Transferir o modelo para a tradução no dispositivo.</string>\n    <string name=\"delete_translation_model\">Apagar o modelo da tradução</string>\n    <string name=\"online\">Online</string>\n    <string name=\"profile\">Perfil</string>\n    <string name=\"correct_answers\">Respostas corretas</string>\n    <string name=\"total_questions\">Total de perguntas</string>\n    <string name=\"last_quiz_times\">Últimos tempos de quiz</string>\n    <string name=\"time\">Tempo</string>\n    <string name=\"last_questions\">Ultimas perguntas</string>\n    <string name=\"correct_words\">Palavras corretas</string>\n    <string name=\"total_words\">Palavras totais</string>\n    <string name=\"user_xp\">XP do usuário</string>\n    <string name=\"level\">Nível</string>\n    <string name=\"current_xp\">XP Atual</string>\n    <string name=\"required_xp_to_next_level\">XP requerido para o próximo nível</string>\n    <string name=\"photo_of_s\">Foto de %1$s</string>\n    <string name=\"good_morning\">Bom dia</string>\n    <string name=\"good_afternoon\">Boa tarde</string>\n    <string name=\"good_evening\">Boa noite</string>\n    <string name=\"good_night\">Boa noite</string>\n    <string name=\"no_diamonds\">Sem diamantes</string>\n    <string name=\"skip_question_q\">Saltar pergunta?</string>\n    <plurals name=\"you_have_n_diamonds_skip_question_q\" tools:ignore=\"MissingQuantity,UnusedQuantity\">\n        <item quantity=\"one\">Tens %1$d diamante disponível, queres usar o diamante para saltar esta pergunta\\?</item>\n        <item quantity=\"many\">Tens %1$d diamantes disponíveis, queres usar %2$d diamantes para saltar esta pergunta\\?</item>\n        <item quantity=\"other\">Tens %1$d diamantes disponíveis, queres usar %2$d diamantes para saltar esta pergunta\\?</item>\n    </plurals>\n    <string name=\"skip\">Saltar</string>\n    <string name=\"save\">Guardar</string>\n    <string name=\"dismiss\">Dispensar</string>\n    <string name=\"more_options\">Mais opções</string>\n    <string name=\"open_drawer\">Abrir navegação</string>\n    <string name=\"copy_maze_seed\">Copiar seed labirinto</string>\n    <string name=\"restart_maze\">Recomeçar labirinto</string>\n    <string name=\"generate_maze\">Gerar labirinto</string>\n    <string name=\"random_maze\">Labirinto aleatório</string>\n    <string name=\"generate_maze_with_random_items\">Gerar labirinto com perguntas aleatórias, todos os modos de jogo serão incluidos.</string>\n    <string name=\"custom_maze\">Labirinto customizado</string>\n    <string name=\"generate_maze_with_custom_options\">Gerar labirinto com opções personalizadas</string>\n    <string name=\"expand_custom_options\">Expandir opções de personalização</string>\n    <string name=\"custom_maze_multi_choice_warning\">Ao usar uma seed personalizada, as perguntas de múltipla escolha não serão geradas aleatoriamente por essa semente.</string>\n    <string name=\"generate\">Gerar</string>\n    <string name=\"seed\">Seed</string>\n    <string name=\"maze_completed\">Labirinto completo!</string>\n    <string name=\"generate_offline_maze\">Gerar labirinto offline</string>\n    <string name=\"generate_offline_maze_description\">Gerar labirinto com todos os modos de jogo que não requerem conecção com a internet.</string>\n    <string name=\"user\">Usuário</string>\n    <string name=\"spanish\">Espanhol</string>\n    <string name=\"french\">Francês</string>\n    <string name=\"number_trivia\">Curiosidades sobre números</string>\n    <string name=\"about_and_help\">Sobre e ajuda</string>\n    <string name=\"animations\">Animações</string>\n    <string name=\"animations_enabled\">Animações habilitadas</string>\n    <string name=\"global_animations_enabled\">Animações globais ativadas</string>\n    <string name=\"wordle_animations_enabled\">Animações do Wordle ativadas</string>\n    <string name=\"analytics\">Dados de Análise</string>\n    <string name=\"analytics_collection_enabled\">Coleta de análise ativada</string>\n    <string name=\"sort_questions\">Ordenar perguntas</string>\n    <string name=\"play_with_random_saved_questions\">Jogar quiz com as perguntas guardadas</string>\n    <string name=\"play\">Jogar</string>\n    <string name=\"play_with_selected_questions\">Jogar com as perguntas selecionadas</string>\n    <string name=\"unselect_all\">Desmarcar Tudo</string>\n    <string name=\"select_all\">Selecionar Tudo</string>\n    <string name=\"delete_selected\">Apagar Selecionados</string>\n    <string name=\"download_questions\">Transferir perguntas</string>\n    <string name=\"sort_by_default\">Ordenar por Padrão</string>\n    <string name=\"sort_by_description\">Ordenar pela Descrição</string>\n    <string name=\"sort_by_category\">Ordenar pela Categoria</string>\n    <string name=\"country_capital_flags\">Bandeiras da capital do país</string>\n    <string name=\"comparison_quiz\">Jogo de Comparação</string>\n    <string name=\"or\">ou</string>\n    <string name=\"position_n\">Posição: %1$d</string>\n    <string name=\"highest_n\">Melhor: %1$d</string>\n    <string name=\"your_score\">Sua pontuação</string>\n    <string name=\"highest_score\">Melhor pontuação</string>\n    <string name=\"select_a_comparison_mode_for_the_first_item\">Selecione um modo de comparação para o primeiro item</string>\n    <string name=\"image_category_of_s\">Imagem da categoria %1$s</string>\n    <string name=\"icon_of_s\">Icon de %1$s</string>\n    <string name=\"greater\">Maior</string>\n    <string name=\"lesser\">Menor</string>\n    <string name=\"email\">Email</string>\n    <string name=\"password\">Palavra Passe</string>\n    <string name=\"name\">Nome</string>\n    <string name=\"next\">Continuar</string>\n    <string name=\"random_quiz\">Quiz Aleatório</string>\n    <string name=\"random\">Aleatório</string>\n    <string name=\"quiz_with_random_categories\">Quiz com categorias aleatórias</string>\n    <string name=\"see_all_categories\">Ver todas as categorias</string>\n    <string name=\"see_less_categories\">Ver menos categorias</string>\n    <string name=\"clear_recent_categories\">Limpar categorias recentes</string>\n    <string name=\"clear_recent_categories_description\">Remova todas as categorias recentes da tela de lista</string>\n    <string name=\"guess_the_number\">Adivinha o Número</string>\n    <string name=\"data_collection_consent\">Consentimento de coleta de dados</string>\n    <string name=\"data_collection_consent_description\">\n        Obrigado por usares o NewQuiz! Para ajudar-nos a melhorar a aplicação e corrigir quaisquer problemas que possam surgir, nós coletamos alguns dados do seu dispositivo. Os dados que coletamos incluem:\\n\\n\n        \\t•   Dados analíticos: incluem informações sobre como você usa a aplicação, como quais recursos você usa com mais frequência, quanto tempo você gasta em cada página e quantas perguntas você conclui.\\n\\n\n        \\t•   Dados de falha: se a aplicação travar enquanto você o estiver a usar, coletamos dados sobre a falha para nos ajudar a identificar e corrigir o problema.\\n\\n\n        \\t•   Dados de desempenho: coletamos informações sobre o desempenho da aplicação em seu dispositivo, como a rapidez com que as páginas são carregadas e quanto tempo leva para concluir as perguntas.\\n\\n\n        Levamos sua privacidade a sério e usamos esses dados apenas para fins de aprimoramento da aplicação. Seus dados nunca serão vendidos ou compartilhados com terceiros para fins de marketing.\\n\\n\n        Ao usar o NewQuiz, você concorda com a coleta desses dados. Se mudar de ideia, você pode desativar a coleta de dados nas configurações do app.\\n\\n\n        Obrigado pela sua compreensão e apoio!\n    </string>\n    <string name=\"data_collection_consent_agree\">Sim, eu concordo</string>\n    <string name=\"data_collection_consent_disagree\">Não, eu não concordo</string>\n    <string name=\"general_analytics_enabled\">Análises gerais ativadas</string>\n    <string name=\"general_analytics_description_enabled\">Ativa análises gerais, como eventos, por exemplo: início do jogo, etc.</string>\n    <string name=\"crash_analytics_enabled\">Análises de falhas ativadas</string>\n    <string name=\"crash_analytics_description_enabled\">Ativa análises de dados de falhas, como falhas de aplicações e exceções.</string>\n    <string name=\"performance_monitoring_enabled\">Monitoramento de desempenho ativado</string>\n    <string name=\"performance_monitoring_description_enabled\">Ativa o monitoramento de desempenho, como tempo de inicialização da aplicação e outras métricas de desempenho.</string>\n    <plurals name=\"play_random_multi_choice_quiz_game\">\n        <item quantity=\"one\">Jogue %d jogo de escolha multipla aleatório.</item>\n        <item quantity=\"other\">Jogue %d jogos de escolha múltipla aleatórios.</item>\n        <item quantity=\"many\">Jogue %d jogos de escolha múltipla aleatórios.</item>\n    </plurals>\n    <plurals name=\"end_multi_choice_quiz_game\">\n        <item quantity=\"one\">Termine %d jogo de escolha multipla aleatório.</item>\n        <item quantity=\"other\">Termine %d jogos de escolha múltipla aleatórios.</item>\n        <item quantity=\"many\">Termine %d jogos de escolha múltipla aleatórios.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_questions\">\n        <item quantity=\"one\">Responda a %d pergunta no jogo de escolha múltipla.</item>\n        <item quantity=\"other\">Responda a %d perguntas no jogo de escolha múltipla.</item>\n        <item quantity=\"many\">Responda a %d perguntas no jogo de escolha múltipla.</item>\n    </plurals>\n    <plurals name=\"get_multi_choice_quiz_answer_correct\">\n        <item quantity=\"one\">Obtenha %d resposta correta no jogo de escolha múltipla.</item>\n        <item quantity=\"other\">Obtenha %d respostas corretas no jogo de escolha múltipla.</item>\n        <item quantity=\"many\">Obtenha %d respostas corretas no jogo de escolha múltipla.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_game_in_category\">\n        <item quantity=\"one\">Jogue um jogo na categoria de %2$s no jogo de escolha múltipla.</item>\n        <item quantity=\"other\">Jogue %1$d jogos na categoria de %2$s no jogo de escolha múltipla.</item>\n        <item quantity=\"many\">Jogue %1$d jogos na categoria de %2$s no jogo de escolha múltipla.</item>\n    </plurals>\n    <plurals name=\"get_wordle_word_correct\">\n        <item quantity=\"one\">Acertar %d palavra no jogo Wordle.</item>\n        <item quantity=\"other\">Acertar %d palavras no jogo Wordle.</item>\n        <item quantity=\"many\">Acertar %d palavras no jogo Wordle.</item>\n    </plurals>\n    <plurals name=\"play_wordle_game_in_category\">\n        <item quantity=\"one\">Jogar um jogo com a categoria %2$s no jogo Wordle.</item>\n        <item quantity=\"other\">Jogar %1$d jogos com a categoria %2$s no jogo Wordle.</item>\n        <item quantity=\"many\">Jogar %1$d jogos com a categoria %2$s no jogo Wordle.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_category\">\n        <item quantity=\"one\">Jogar um jogo com a categoria %2$s no jogo de comparação.</item>\n        <item quantity=\"other\">Jogar %1$d jogos com a categoria %2$s no jogo de comparação.</item>\n        <item quantity=\"many\">Jogar %1$d jogos com a categoria %2$s no jogo de comparação.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_and_get_score\">\n        <item quantity=\"one\">Jogar um jogo e obter uma pontuação de %2$d no jogo de comparação.</item>\n        <item quantity=\"other\">Jogar %1$d jogos e obter uma pontuação de %2$d no jogo de comparação.</item>\n        <item quantity=\"many\">Jogar %1$d jogos e obter uma pontuação de %2$d no jogo de comparação.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_comparison_mode\">\n        <item quantity=\"one\">Jogar um jogo no modo %2$s no jogo de comparação.</item>\n        <item quantity=\"other\">Jogar %1$d jogos no modo %2$s no jogo de comparação.</item>\n        <item quantity=\"many\">Jogar %1$d jogos no modo %2$s no jogo de comparação.</item>\n    </plurals>\n    <string name=\"daily_challenge\">Desafio Diário</string>\n    <string name=\"no_diamonds_to_skip_question_description\">Não tens diamantes para saltar esta pergunta!</string>\n    <string name=\"wordle_animations_enabled_description\">Ativa as animações para o jogo wordle.</string>\n    <string name=\"multi_choice_animations_enabled\">Animações ativas para o jogo de escolha multipla</string>\n    <string name=\"multi_choice_animations_enabled_description\">Ativa as animações para o jogo de escolha multipla.</string>\n    <string name=\"quiz_steps_row_container\">Container das etapas do quiz</string>\n    <string name=\"question_n_correct\">Pergunta %1$d - Correta</string>\n    <string name=\"question_n_incorrect\">Pergunta %1$d - Incorreta</string>\n    <string name=\"loading_questions\">A carregar as perguntas…</string>\n    <string name=\"hide_online_categories\">Ocultar categorias online</string>\n    <string name=\"hide_online_categories_description\">Ocultar categorias que requerem conexão com a internet, quando o dispositivo está offline.</string>\n    <string name=\"no_categories_available\">Sem categorias disponíveis</string>\n    <string name=\"target_language\">Idioma da Tradução</string>\n    <string name=\"target_language_description\">Selecione o idioma para o qual deseja traduzir</string>\n    <string name=\"download_settings\">Configurações de download</string>\n    <string name=\"require_wifi\">Requer Wi-Fi</string>\n    <string name=\"translation_require_wifi_description\">Transfira o modelo de tradução apenas quando estiver ligado a uma rede Wi-Fi.</string>\n    <string name=\"require_charging\">Requer carregamento</string>\n    <string name=\"translation_require_charging_description\">Transfira o modelo de tradução apenas quando o dispositivo estiver a carregar.</string>\n    <string name=\"confirm\">Confirmar</string>\n    <string name=\"downloading_translation_model\">A transferir o modelo de tradução</string>\n    <string name=\"downloading_translation_model_description\">A transferir o modelo de tradução, esta operação pode demorar algum tempo.</string>\n    <string name=\"others\">Outros</string>\n    <string name=\"require_internet_connection\">Requer ligação à Internet</string>\n    <string name=\"dont_require_internet_connection\">Não requer ligação à Internet</string>\n    <string name=\"none\">Nenhum</string>\n    <string name=\"both\">Ambos</string>\n    <string name=\"show_category_connection_info\">Mostrar informações de conexão da categoria</string>\n    <string name=\"xp\">XP</string>\n    <string name=\"total_xp\">Total de XP</string>\n    <string name=\"today\">Hoje</string>\n    <string name=\"this_week\">Esta Semana</string>\n    <string name=\"general_settings_summary\">Categorias, Animações</string>\n    <string name=\"multi_choice_settings_summary\">Contagem de perguntas</string>\n    <string name=\"wordle_settings_summary\">Modo difícil, Linguagem do questionário, Dicas de palavras</string>\n    <string name=\"translation_settings_summary\">Idioma de tradução</string>\n    <string name=\"about_and_help_settings_summary\">Versão, Contacto, Licenças de código aberto</string>\n    <string name=\"select_only_offline\">Seleciona apenas Offline</string>\n    <string name=\"maze_settings_auto_scroll_title\">Desloca-se automaticamente para a pergunta atual</string>\n    <string name=\"maze_settings_auto_scroll_summary\">Desloca-se automaticamente para a pergunta atual ao jogar o labirinto.</string>\n    <string name=\"remaining_time\">Restante: %1$s</string>\n    <string name=\"claim_n_diamonds\">Reivindique %1$d \\uD83D\\uDC8E</string>\n    <string name=\"regional_preferences\">Preferências regionais</string>\n    <string name=\"temperature_unit\">Unidade de Temperatura</string>\n    <string name=\"system_default\">Padrão do sistema</string>\n    <string name=\"celsius\">Celsius</string>\n    <string name=\"fahrenheit\">Fahrenheit</string>\n    <string name=\"distance_unit\">Unidade de distância</string>\n    <string name=\"metric\">Métrica</string>\n    <string name=\"imperial\">Imperiais</string>\n    <string name=\"no_data\">Sem dados</string>\n    <string name=\"no_questions_saved_yet\">Ainda não há perguntas guardadas!</string>\n    <string name=\"no_questions_saved_description\">Guarda ou transfire perguntas para começar a jogar offline.</string>\n    <string name=\"level_completed\">Nível concluído!</string>\n    <string name=\"next_level\">Próximo Nível</string>\n    <string name=\"main_menu\">Menu Principal</string>\n    <string name=\"try_again\">Tentar Novamente</string>\n    <string name=\"level_failed\">Nível falhado!</string>\n    <string name=\"level_failed_description\">Não desistas! Tenta novamente vencer este nível.</string>\n    <string name=\"error_verifying_word\">Erro ao verificar a palavra!</string>\n    <string name=\"need_to_use_all_hints_error\">Precisas usar todas as dicas da última linha!</string>\n    <string name=\"empty_word\">Palavra vazia</string>\n    <string name=\"word_not_only_letters_error\">A palavra deve conter apenas letras</string>\n    <string name=\"word_not_only_digits_error\">A palavra deve conter apenas dígitos</string>\n    <string name=\"word_invalid_math_formula_error\">A fórmula da esquerda não é igual à solução da direita</string>\n    <string name=\"invalid_questions\">Perguntas inválidas</string>\n    <string name=\"invalid_questions_description\">Existem %1$d perguntas com categorias inválidas. Remova as perguntas para continuar a jogar ou reinicie o labirinto.</string>\n    <string name=\"remove_them\">Remova-as</string>\n    <string name=\"category_information\">Informações de categoria</string>\n    <string name=\"cancel\">Cancelar</string>\n    <string name=\"restart\">Reiniciar</string>\n    <string name=\"restart_maze_dialog_description\">Tens a certeza de que queres reiniciar o labirinto?</string>\n</resources>"
  },
  {
    "path": "core/src/main/res/values-ta/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"entertainment_musicals_and_theatres\">இசைக்கலைஞர்கள் &amp; தியேட்டர்கள்</string>\n    <string name=\"entertainment_television\">தொலைக்காட்சி</string>\n    <string name=\"entertainment_video_games\">வீடியோ கேம்கள்</string>\n    <string name=\"entertainment_board_games\">பலகை விளையாட்டுகள்</string>\n    <string name=\"science_and_nature\">அறிவியல் &amp; இயற்கை</string>\n    <string name=\"science_computers\">கணினிகள்</string>\n    <string name=\"science_mathematics\">கணிதம்</string>\n    <string name=\"mythology\">புராணம்</string>\n    <string name=\"sports\">விளையாட்டு</string>\n    <string name=\"is_in_the_word_but_in_the_wrong_spot_wordle\">\" வார்த்தையில் உள்ளது, ஆனால் தவறான இடத்தில்.\"</string>\n    <string name=\"profile\">சுயவிவரம்</string>\n    <string name=\"correct_answers\">சரியான பதில்கள்</string>\n    <string name=\"total_questions\">மொத்த கேள்விகள்</string>\n    <string name=\"last_quiz_times\">கடைசி நொடி வினா நேரங்கள்</string>\n    <string name=\"time\">நேரம்</string>\n    <string name=\"last_questions\">கடைசி கேள்விகள்</string>\n    <string name=\"hard\">கடினமானது</string>\n    <string name=\"open_drawer\">திறந்த அலமாரியை</string>\n    <string name=\"copy_maze_seed\">பிரமை விதை நகலெடுக்கவும்</string>\n    <string name=\"restart_maze\">பிரமை மறுதொடக்கம் செய்யுங்கள்</string>\n    <string name=\"generate_maze\">பிரமை உருவாக்குங்கள்</string>\n    <string name=\"maze_completed\">பிரமை முடிந்தது!</string>\n    <string name=\"generate_offline_maze\">இணைப்பில்லாத பிரமை உருவாக்குங்கள்</string>\n    <string name=\"generate_offline_maze_description\">இணைய இணைப்பு தேவையில்லாத அனைத்து விளையாட்டு முறைகளுடனும் பிரமை உருவாக்குங்கள்.</string>\n    <string name=\"random_maze\">சீரற்ற பிரமை</string>\n    <string name=\"custom_maze\">தனிப்பயன் பிரமை</string>\n    <string name=\"icon_of_s\">%1$s படவுரு</string>\n    <string name=\"try_again\">மீண்டும் முயற்சிக்கவும்</string>\n    <string name=\"level_failed\">நிலை தோல்வியடைந்தது!</string>\n    <string name=\"level_failed_description\">விட்டுவிடாதீர்கள்! இந்த நிலையை வெல்ல மீண்டும் முயற்சிக்கவும்.</string>\n    <string name=\"error_verifying_word\">சொல் சரிபார்ப்பது!</string>\n    <string name=\"need_to_use_all_hints_error\">கடைசி வரிசையில் இருந்து அனைத்து குறிப்புகளையும் நீங்கள் பயன்படுத்த வேண்டும்!</string>\n    <string name=\"empty_word\">வெற்று சொல்</string>\n    <string name=\"word_not_only_letters_error\">வார்த்தையில் எழுத்துக்கள் மட்டுமே இருக்க வேண்டும்</string>\n    <string name=\"word_not_only_digits_error\">வார்த்தையில் இலக்கங்கள் மட்டுமே இருக்க வேண்டும்</string>\n    <string name=\"word_invalid_math_formula_error\">இடது தேற்றம் வலது தீர்வுக்கு சமமானதல்ல</string>\n    <string name=\"invalid_questions\">தவறான கேள்விகள்</string>\n    <string name=\"invalid_questions_description\">தவறான வகைகளுடன் %1$d கேள்விகள் உள்ளன. தொடர்ந்து விளையாடுவதற்கு கேள்விகளை அகற்றவும் அல்லது பிரமை மறுதொடக்கம் செய்யவும்.</string>\n    <string name=\"remove_them\">அவற்றை அகற்றவும்</string>\n    <string name=\"category_information\">வகை செய்தி</string>\n    <string name=\"cancel\">ரத்துசெய்</string>\n    <string name=\"restart\">மறுதொடக்கம்</string>\n    <string name=\"restart_maze_dialog_description\">நீங்கள் பிரமை மறுதொடக்கம் செய்ய விரும்புகிறீர்களா?</string>\n    <string name=\"quick_quiz\">விரைவான நொடி வினா</string>\n    <string name=\"back\">பின்</string>\n    <string name=\"more_options\">மேலும் விருப்பங்கள்</string>\n    <string name=\"saved_questions\">சேமித்த கேள்விகள்</string>\n    <string name=\"wordle_infinite\">வேர்டில் எல்லையற்றது</string>\n    <string name=\"wordle_daily\">நாள்தோறும் வேர்டில்</string>\n    <string name=\"verify\">சரிபார்க்கவும்</string>\n    <string name=\"play_again\">மீண்டும் விளையாடுங்கள்</string>\n    <string name=\"close\">மூடு</string>\n    <string name=\"game_over\">விளையாட்டு ஓவர்</string>\n    <string name=\"item_empty\">உருப்படி காலியாக உள்ளது</string>\n    <string name=\"item_i_none\">பொருள் %1$s எதுவுமில்லை</string>\n    <string name=\"item_i_present\">பொருள் %1$s உள்ளன</string>\n    <string name=\"item_i_correct\">பொருள் %1$s சரியானவை</string>\n    <string name=\"four_letters\">4 கடிதங்கள்</string>\n    <string name=\"five_letters\">5 கடிதங்கள்</string>\n    <string name=\"six_letters\">6 கடிதங்கள்</string>\n    <string name=\"back_month\">பின் மாதம்</string>\n    <string name=\"next_month\">அடுத்த மாதம்</string>\n    <string name=\"settings\">அமைப்புகள்</string>\n    <string name=\"general\">பொது</string>\n    <string name=\"quiz\">நொடி வினா</string>\n    <string name=\"quiz_language\">நொடி வினா மொழி</string>\n    <string name=\"english\">ஆங்கிலம்</string>\n    <string name=\"portuguese\">போர்த்துகீசியம்</string>\n    <string name=\"spanish\">ச்பானிச்</string>\n    <string name=\"french\">பிரஞ்சு</string>\n    <string name=\"color_blind_mode\">வண்ண குருட்டு முறை</string>\n    <string name=\"high_contrast_colors\">அதிக மாறுபட்ட வண்ணங்கள்</string>\n    <string name=\"info\">தகவல்</string>\n    <string name=\"letter_hints\">கடிதம் குறிப்புகள்</string>\n    <string name=\"hint_above_the_letter_that_it_appears_twice_or_more_in_the_hidden_word\">மறைக்கப்பட்ட வார்த்தையில் இரண்டு அல்லது அதற்கு மேற்பட்டதாகத் தோன்றும் கடிதத்திற்கு மேலே குறிக்கவும்</string>\n    <string name=\"hard_mode\">ஆர்ட் பயன்முறை</string>\n    <string name=\"any_revealed_hints_must_be_used_in_subsequest_guesses\">வெளிப்படுத்தப்பட்ட குறிப்புகள் அடுத்தடுத்த யூகங்களில் பயன்படுத்தப்பட வேண்டும்</string>\n    <string name=\"rows_limited\">வரிசைகள் லிமிடெட்</string>\n    <string name=\"wordle_infinite_row_limited\">வேர்டில் எல்லையற்ற வரிசை லிமிடெட்.</string>\n    <string name=\"wordle_infinite_row_limit_value\">வேர்டில் எல்லையற்ற வரிசை வரம்பு மதிப்பு.</string>\n    <string name=\"clean_calendar_data\">காலண்டர் தரவு தூய்மையான</string>\n    <string name=\"clean_saved_calendar_wins_losses\">சேமித்த அனைத்து காலண்டர் வெற்றிகளையும்/இழப்புகளையும் சுத்தப்படுத்துகிறது.</string>\n    <string name=\"clear_settings\">தெளிவான விருப்பத்தேர்வுகள்</string>\n    <string name=\"remove_all_saved_settings\">சேமித்த அனைத்து அமைப்புகளையும் அகற்றவும்</string>\n    <string name=\"home\">வீடு</string>\n    <string name=\"today_game\">இன்று விளையாட்டு</string>\n    <string name=\"today_random_game\">இன்று சீரற்ற விளையாட்டு</string>\n    <string name=\"multi_choice_quiz\">மல்டி சாய்ச் நொடி வினா</string>\n    <string name=\"flag_quiz\">கொடி நொடி வினா</string>\n    <string name=\"logo_quiz\">லோகோ நொடி வினா</string>\n    <string name=\"other\">மற்றொன்று</string>\n    <string name=\"general_knowledge\">பொது அறிவு</string>\n    <string name=\"entertainment_books\">புத்தகங்கள்</string>\n    <string name=\"entertainment_film\">படம்</string>\n    <string name=\"entertainment_music\">இசை</string>\n    <string name=\"geography\">புவியியல்</string>\n    <string name=\"history\">வரலாறு</string>\n    <string name=\"politics\">அரசியல்</string>\n    <string name=\"art\">கலை</string>\n    <string name=\"celebrities\">பிரபலங்கள்</string>\n    <string name=\"animals\">விலங்குகள்</string>\n    <string name=\"vehicles\">வாகனங்கள்</string>\n    <string name=\"entertainment_comics\">காமிக்ச்</string>\n    <string name=\"science_gadgets\">கேசெட்டுகள்</string>\n    <string name=\"entertainment_japanese_anime_and_manga\">சப்பானிய அனிம் &amp; மங்கா</string>\n    <string name=\"entertainment_cartoon_and_animations\">கார்ட்டூன் &amp; அனிமேசன்கள்</string>\n    <string name=\"categories\">வகைகள்</string>\n    <plurals name=\"n_questions_available\">\n        <item quantity=\"one\">%1$s கேள்வி கிடைக்கிறது</item>\n        <item quantity=\"other\">%1$s கேள்விகள் உள்ளன</item>\n    </plurals>\n    <string name=\"see_all\">அனைத்தையும் காண்க</string>\n    <string name=\"quiz_question_size\">நொடி வினா கேள்வி அளவு</string>\n    <string name=\"translation\">மொழிபெயர்ப்பு</string>\n    <string name=\"difficulty\">தொல்லை</string>\n    <string name=\"is_not_in_the_target_word_wordle\">\" இலக்கு வார்த்தையில் இல்லை.\"</string>\n    <string name=\"is_in_the_word_and_in_the_correct_spot_wordle\">\" வார்த்தையில் மற்றும் சரியான இடத்தில் உள்ளது.\"</string>\n    <string name=\"results_screen\">முடிவுகள் திரை</string>\n    <string name=\"n_correct_questions\">%1$s சரியான கேள்விகள்</string>\n    <string name=\"translation_enabled\">மொழிபெயர்ப்பு இயக்கப்பட்டது</string>\n    <string name=\"download_translation_model\">மொழிபெயர்ப்பு மாதிரியைப் பதிவிறக்கவும்</string>\n    <string name=\"download_translation_model_description\">சாதன மொழிபெயர்ப்பில் மொழிபெயர்ப்பு மாதிரியைப் பதிவிறக்கவும்.</string>\n    <string name=\"downloading_translation_model\">மொழிபெயர்ப்பு மாதிரியைப் பதிவிறக்குகிறது</string>\n    <string name=\"downloading_translation_model_description\">மொழிபெயர்ப்பு மாதிரியைப் பதிவிறக்குகிறது, இதற்கு சிறிது நேரம் ஆகலாம்.</string>\n    <string name=\"delete_translation_model\">மொழிபெயர்ப்பு மாதிரியை நீக்கு</string>\n    <string name=\"online\">ஆன்லைனில்</string>\n    <string name=\"correct_words\">சரியான சொற்கள்</string>\n    <string name=\"total_words\">மொத்த சொற்கள்</string>\n    <string name=\"user_xp\">பயனர் எக்ச்பி</string>\n    <string name=\"level\">நிலை</string>\n    <string name=\"current_xp\">தற்போதைய எக்ச்பி</string>\n    <string name=\"required_xp_to_next_level\">எக்ச்பி அடுத்த நிலைக்கு தேவை</string>\n    <string name=\"photo_of_s\">%1$s புகைப்படம்</string>\n    <string name=\"good_morning\">காலை வணக்கம்</string>\n    <string name=\"good_afternoon\">நல்ல மதியம்</string>\n    <string name=\"good_evening\">நல்ல மாலை</string>\n    <string name=\"good_night\">நல்ல இரவு</string>\n    <string name=\"no_diamonds\">வைரங்கள் இல்லை</string>\n    <string name=\"skip_question_q\">கேள்வியைத் தவிர்க்கவா?</string>\n    <plurals name=\"you_have_n_diamonds_skip_question_q\">\n        <item quantity=\"one\">உங்களிடம் ஒரு வைரம் உள்ளது, இந்த கேள்வியைத் தவிர்க்க வைரத்தைப் பயன்படுத்த விரும்புகிறீர்களா?</item>\n        <item quantity=\"other\">உங்களிடம் %1$d வைரங்கள் உள்ளன, இந்த கேள்வியைத் தவிர்க்க %2$d வைரங்களைப் பயன்படுத்த விரும்புகிறீர்களா?</item>\n    </plurals>\n    <string name=\"no_diamonds_to_skip_question_description\">இந்த கேள்வியைத் தவிர்க்க உங்களுக்கு வைரங்கள் இல்லை!</string>\n    <string name=\"skip\">தவிர்</string>\n    <string name=\"save\">சேமி</string>\n    <string name=\"dismiss\">தள்ளுபடி</string>\n    <string name=\"guess_the_word\">வார்த்தையை யூகிக்கவும்</string>\n    <string name=\"guess_the_number\">எண்ணை யூகிக்கவும்</string>\n    <string name=\"guess_math_formula\">கணித சூத்திரத்தை யூகிக்கவும்</string>\n    <string name=\"guess_solution\">தீர்வு யூகிக்கவும்</string>\n    <string name=\"math_quiz\">கணித நொடி வினா</string>\n    <string name=\"maze\">பிரமை</string>\n    <string name=\"easy\">எளிதானது</string>\n    <string name=\"medium\">சராசரி</string>\n    <string name=\"generate_maze_with_random_items\">சீரற்ற கேள்விகளுடன் பிரமை உருவாக்குங்கள், அனைத்து விளையாட்டு முறைகளும் சேர்க்கப்படும்.</string>\n    <string name=\"generate_maze_with_custom_options\">தனிப்பயன் விருப்பங்களுடன் பிரமை உருவாக்குங்கள்</string>\n    <string name=\"expand_custom_options\">தனிப்பயன் விருப்பங்களை விரிவாக்குங்கள்</string>\n    <string name=\"custom_maze_multi_choice_warning\">தனிப்பயன் விதை பயன்படுத்தும் போது, மல்டி சாய்ச் கேள்விகள் இந்த விதை மூலம் சீரற்றதாக இருக்காது.</string>\n    <string name=\"generate\">உருவாக்கு</string>\n    <string name=\"seed\">விதை</string>\n    <string name=\"user\">பயனர்</string>\n    <string name=\"number_trivia\">எண் அற்பம்</string>\n    <string name=\"about_and_help\">பற்றி மற்றும் உதவி</string>\n    <string name=\"animations\">அனிமேசன்கள்</string>\n    <string name=\"animations_enabled\">அனிமேசன்கள் இயக்கப்பட்டன</string>\n    <string name=\"global_animations_enabled\">உலகளாவிய அனிமேசன்கள் இயக்கப்பட்டன</string>\n    <string name=\"wordle_animations_enabled\">வேர்டில் அனிமேசன்கள் இயக்கப்பட்டன</string>\n    <string name=\"wordle_animations_enabled_description\">வேர்டல் கேம் பயன்முறைக்கான அனிமேசன்களை இயக்கவும்.</string>\n    <string name=\"multi_choice_animations_enabled\">மல்டி சாய்ச் அனிமேசன்கள் இயக்கப்பட்டன</string>\n    <string name=\"multi_choice_animations_enabled_description\">மல்டி சாய்ச் கேம் பயன்முறையில் அனிமேசன்களை இயக்கவும்.</string>\n    <string name=\"sort_questions\">கேள்விகளை வரிசைப்படுத்துங்கள்</string>\n    <string name=\"play_with_random_saved_questions\">சீரற்ற சேமித்த கேள்விகளுடன் நொடி வினா விளையாடுங்கள்</string>\n    <string name=\"play\">விளையாடுங்கள்</string>\n    <string name=\"play_with_selected_questions\">தேர்ந்தெடுக்கப்பட்ட கேள்விகளுடன் விளையாடுங்கள்</string>\n    <string name=\"unselect_all\">அனைத்தையும் தேர்வு செய்யுங்கள்</string>\n    <string name=\"select_all\">அனைத்தையும் தெரிவுசெய்</string>\n    <string name=\"delete_selected\">தேர்ந்தெடுக்கப்பட்டதை நீக்கு</string>\n    <string name=\"download_questions\">கேள்விகளைப் பதிவிறக்கவும்</string>\n    <string name=\"sort_by_default\">இயல்பாக வரிசைப்படுத்தவும்</string>\n    <string name=\"sort_by_description\">விளக்கத்தால் வரிசைப்படுத்துங்கள்</string>\n    <string name=\"sort_by_category\">வகை அடிப்படையில் வரிசைப்படுத்துங்கள்</string>\n    <string name=\"country_capital_flags\">நாட்டின் மூலதனக் கொடிகள்</string>\n    <string name=\"comparison_quiz\">ஒப்பீட்டு நொடி வினா</string>\n    <string name=\"or\">அல்லது</string>\n    <string name=\"position_n\">நிலை: %1$d</string>\n    <string name=\"highest_n\">அதிகபட்சம்: %1$d</string>\n    <string name=\"your_score\">உங்கள் மதிப்பெண்</string>\n    <string name=\"highest_score\">அதிக மதிப்பெண்</string>\n    <string name=\"select_a_comparison_mode_for_the_first_item\">முதல் உருப்படிக்கு ஒப்பீட்டு பயன்முறையைத் தேர்ந்தெடுக்கவும்</string>\n    <string name=\"image_category_of_s\">பட வகை %1$s</string>\n    <string name=\"greater\">பெரிய</string>\n    <string name=\"lesser\">குறைவான</string>\n    <string name=\"email\">மின்னஞ்சல்</string>\n    <string name=\"password\">கடவுச்சொல்</string>\n    <string name=\"name\">பெயர்</string>\n    <string name=\"next\">அடுத்தது</string>\n    <string name=\"random_quiz\">சீரற்ற நொடி வினா</string>\n    <string name=\"random\">சீரற்ற</string>\n    <string name=\"quiz_with_random_categories\">சீரற்ற வகைகளுடன் நொடி வினா</string>\n    <string name=\"see_all_categories\">அனைத்து வகைகளையும் காண்க</string>\n    <string name=\"see_less_categories\">குறைவான வகைகளைக் காண்க</string>\n    <string name=\"clear_recent_categories\">அண்மைக் கால வகைகளை அழிக்கவும்</string>\n    <string name=\"clear_recent_categories_description\">பட்டியல் திரையில் இருந்து அனைத்து அண்மைக் கால வகைகளையும் அகற்று</string>\n    <string name=\"loading_questions\">கேள்விகளை ஏற்றுகிறது…</string>\n    <string name=\"quiz_steps_row_container\">நொடி வினா படிகள் கொள்கலன்</string>\n    <string name=\"question_n_correct\">கேள்வி %1$d - சரியானது</string>\n    <string name=\"question_n_incorrect\">கேள்வி %1$d - தவறானது</string>\n    <string name=\"analytics\">பகுப்பாய்வு</string>\n    <string name=\"analytics_collection_enabled\">பகுப்பாய்வு சேகரிப்பு இயக்கப்பட்டது</string>\n    <string name=\"data_collection_consent\">தரவு சேகரிப்பு ஒப்புதல்</string>\n    <string name=\"data_collection_consent_description\">நிய்விசைப் பயன்படுத்தியதற்கு நன்றி! பயன்பாட்டை மேம்படுத்தவும், எழக்கூடிய ஏதேனும் சிக்கல்களை சரிசெய்யவும் எங்களுக்கு உதவ, உங்கள் சாதனத்திலிருந்து சில தரவுகளை நாங்கள் சேகரிக்கிறோம். நாங்கள் சேகரிக்கும் தரவு பின்வருமாறு:\\n\\n • அனலிட்டிக்ச் தரவு: நீங்கள் பயன்பாட்டை எவ்வாறு பயன்படுத்துகிறீர்கள், அதாவது எந்த அம்சங்களை நீங்கள் அடிக்கடி பயன்படுத்துகிறீர்கள், ஒவ்வொரு பக்கத்திலும் எவ்வளவு நேரம் செலவிடுகிறீர்கள், எந்த நொடி வினாக்களை முடிக்கிறீர்கள் என்பது பற்றிய தகவல்கள் இதில் அடங்கும்.\\n\\n தகவல்கள் தரவு செயலிழப்பு: நீங்கள் அதைப் பயன்படுத்தும் போது பயன்பாடு செயலிழந்தால், சிக்கலை அடையாளம் காணவும் சரிசெய்யவும் எங்களுக்கு உதவ விபத்து பற்றிய தரவை நாங்கள் சேகரிக்கிறோம்.\\n\\n • செயல்திறன் தரவு: உங்கள் சாதனத்தில் பயன்பாடு எவ்வாறு செயல்படுகிறது என்பது பற்றிய தகவல்களை நாங்கள் சேகரிக்கிறோம், அதாவது பக்கங்கள் எவ்வளவு விரைவாக ஏற்றப்படுகின்றன, நொடி வினாக்களை முடிக்க எவ்வளவு நேரம் ஆகும்.\\n\\n நாங்கள் உங்கள் தனியுரிமையை தீவிரமாக எடுத்துக்கொள்கிறோம், பயன்பாட்டு மேம்பாட்டு நோக்கங்களுக்காக இந்தத் தரவை மட்டுமே பயன்படுத்துகிறோம். சந்தைப்படுத்தல் நோக்கங்களுக்காக உங்கள் தரவு ஒருபோதும் மூன்றாம் தரப்பினருடன் விற்கப்படாது அல்லது பகிரப்படாது.\\n\\n நியூக்விசைப் பயன்படுத்துவதன் மூலம், இந்தத் தரவின் சேகரிப்புக்கு நீங்கள் ஒப்புக்கொள்கிறீர்கள். நீங்கள் எப்போதாவது உங்கள் எண்ணத்தை மாற்றினால், பயன்பாட்டு அமைப்புகளில் தரவு சேகரிப்பை முடக்கலாம்.\\n\\n உங்கள் புரிதலுக்கும் ஆதரவிற்கும் நன்றி!</string>\n    <string name=\"data_collection_consent_agree\">ஆம், நான் சம்மதிக்கிறேன்</string>\n    <string name=\"data_collection_consent_disagree\">இல்லை, நான் சம்மதிக்கவில்லை</string>\n    <string name=\"general_analytics_enabled\">பொது பகுப்பாய்வு இயக்கப்பட்டது</string>\n    <string name=\"general_analytics_description_enabled\">நிகழ்வுகள், எடுத்துக்காட்டு போன்ற பொதுவான பகுப்பாய்வுகளை செயல்படுத்துகிறது: விளையாட்டு தொடங்குகிறது.</string>\n    <string name=\"crash_analytics_enabled\">செயலிழப்பு பகுப்பாய்வு இயக்கப்பட்டது</string>\n    <string name=\"crash_analytics_description_enabled\">பயன்பாட்டு செயலிழப்புகள் மற்றும் விதிவிலக்குகள் போன்ற செயலிழப்பு தரவு பகுப்பாய்வுகளை செயல்படுத்துகிறது.</string>\n    <string name=\"performance_monitoring_enabled\">செயல்திறன் கண்காணிப்பு இயக்கப்பட்டது</string>\n    <string name=\"performance_monitoring_description_enabled\">பயன்பாட்டு தொடக்க நேரம் மற்றும் பிற செயல்திறன் அளவீடுகள் போன்ற செயல்திறன் கண்காணிப்பை செயல்படுத்துகிறது.</string>\n    <string name=\"daily_challenge\">நாள்தோறும் அறைகூவல்</string>\n    <plurals name=\"play_random_multi_choice_quiz_game\">\n        <item quantity=\"one\">ஒரு சீரற்ற மல்டி சாய்ச் நொடி வினா விளையாட்டை விளையாடுங்கள்.</item>\n        <item quantity=\"other\">%d சீரற்ற மல்டி சாய்ச் நொடி வினா விளையாட்டுகளை விளையாடுங்கள்.</item>\n    </plurals>\n    <plurals name=\"end_multi_choice_quiz_game\">\n        <item quantity=\"one\">ஒரு மல்டி சாய்ச் நொடி வினா விளையாட்டை முடிக்கவும்.</item>\n        <item quantity=\"other\">முடிவு %d மல்டி சாய்ச் நொடி வினா விளையாட்டுகள்.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_questions\">\n        <item quantity=\"one\">மல்டி சாய்ச் நொடி வினா விளையாட்டில் ஒரு கேள்விக்கு பதிலளிக்கவும்.</item>\n        <item quantity=\"other\">மல்டி சாய்ச் நொடி வினா விளையாட்டில் பதில் %d கேள்விகள்.</item>\n    </plurals>\n    <plurals name=\"get_multi_choice_quiz_answer_correct\">\n        <item quantity=\"one\">மல்டி சாய்ச் நொடி வினா விளையாட்டில் ஒரு பதிலை சரியாகப் பெறுங்கள்.</item>\n        <item quantity=\"other\">மல்டி சாய்ச் நொடி வினா விளையாட்டில் %d பதில்களைப் பெறுங்கள்.</item>\n    </plurals>\n    <plurals name=\"play_multi_choice_quiz_game_in_category\">\n        <item quantity=\"one\">மல்டி சாய்ச் நொடி வினாவில் %2$s வகையுடன் ஒரு விளையாட்டை விளையாடுங்கள்.</item>\n        <item quantity=\"other\">மல்டி சாய்ச் நொடி வினாவில் %2$s வகையுடன் %1$d விளையாட்டுகளை விளையாடுங்கள்.</item>\n    </plurals>\n    <plurals name=\"get_wordle_word_correct\">\n        <item quantity=\"one\">வேர்டல் விளையாட்டில் ஒரு வார்த்தையை சரியாகப் பெறுங்கள்.</item>\n        <item quantity=\"other\">வேர்டல் விளையாட்டில் %d சொற்களைப் பெறுங்கள்.</item>\n    </plurals>\n    <plurals name=\"play_wordle_game_in_category\">\n        <item quantity=\"one\">வேர்டல் விளையாட்டில் %2$s வகையுடன் ஒரு விளையாட்டை விளையாடுங்கள்.</item>\n        <item quantity=\"other\">வேர்டல் விளையாட்டில் %2$s வகையுடன் %1$d விளையாட்டுகளை விளையாடுங்கள்.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_category\">\n        <item quantity=\"one\">ஒப்பீட்டு நொடி வினாவில் %2$s வகையுடன் ஒரு விளையாட்டை விளையாடுங்கள்.</item>\n        <item quantity=\"other\">ஒப்பிடுகையில் %2$s வகையுடன் %1$d விளையாட்டுகளை விளையாடுங்கள்.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_and_get_score\">\n        <item quantity=\"one\">ஒரு விளையாட்டை விளையாடுங்கள் மற்றும் ஒப்பீட்டு நொடி வினாவில் %2$d மதிப்பெண்ணைப் பெறுங்கள்.</item>\n        <item quantity=\"other\">%1$d விளையாட்டுகளை விளையாடுங்கள் மற்றும் ஒப்பிடுகையில் %2$d மதிப்பெண்ணைப் பெறுங்கள்.</item>\n    </plurals>\n    <plurals name=\"play_comparison_quiz_game_in_comparison_mode\">\n        <item quantity=\"one\">ஒப்பீட்டு நொடி வினாவில் %2$s பயன்முறையுடன் ஒரு விளையாட்டை விளையாடுங்கள்.</item>\n        <item quantity=\"other\">ஒப்பிடுகையில் %2$s பயன்முறையுடன் %1$d விளையாட்டுகளை விளையாடுங்கள்.</item>\n    </plurals>\n    <string name=\"hide_online_categories\">நிகழ்நிலை வகைகளை மறைக்கவும்</string>\n    <string name=\"hide_online_categories_description\">சாதனம் ஆஃப்லைனில் இருக்கும்போது இணைய இணைப்பு தேவைப்படும் வகைகளை மறைக்கவும்.</string>\n    <string name=\"no_categories_available\">வகைகள் எதுவும் கிடைக்கவில்லை</string>\n    <string name=\"target_language\">இலக்கு மொழி</string>\n    <string name=\"target_language_description\">நீங்கள் மொழிபெயர்க்க விரும்பும் மொழியைத் தேர்ந்தெடுக்கவும்</string>\n    <string name=\"download_settings\">அமைப்புகளை பதிவிறக்கவும்</string>\n    <string name=\"require_wifi\">வைஃபை தேவை</string>\n    <string name=\"translation_require_wifi_description\">WIFI உடன் இணைக்கப்படும்போது மட்டுமே மொழிபெயர்ப்பு மாதிரியை பதிவிறக்கவும்.</string>\n    <string name=\"require_charging\">கட்டணம் வசூலிக்க வேண்டும்</string>\n    <string name=\"translation_require_charging_description\">சாதனம் சார்ச் செய்யும் போது மட்டுமே மொழிபெயர்ப்பு மாதிரியை பதிவிறக்கவும்.</string>\n    <string name=\"confirm\">உறுதிப்படுத்தவும்</string>\n    <string name=\"others\">மற்றவர்கள்</string>\n    <string name=\"require_internet_connection\">இணைய இணைப்பு தேவை</string>\n    <string name=\"dont_require_internet_connection\">இணைய இணைப்பு தேவையில்லை</string>\n    <string name=\"none\">எதுவுமில்லை</string>\n    <string name=\"both\">இரண்டும்</string>\n    <string name=\"show_category_connection_info\">வகை இணைப்பு தகவலைக் காட்டு</string>\n    <string name=\"xp\">எக்ச்பி</string>\n    <string name=\"total_xp\">மொத்த எக்ச்பி</string>\n    <string name=\"today\">இன்று</string>\n    <string name=\"this_week\">இந்த வாரம்</string>\n    <string name=\"general_settings_summary\">வகைகள், அனிமேசன்கள்</string>\n    <string name=\"multi_choice_settings_summary\">கேள்வி எண்ணிக்கை</string>\n    <string name=\"wordle_settings_summary\">கடின பயன்முறை, நொடி வினா மொழி, கடிதம் குறிப்புகள்</string>\n    <string name=\"translation_settings_summary\">மொழிபெயர்ப்பு மொழி</string>\n    <string name=\"about_and_help_settings_summary\">பதிப்பு, தொடர்பு, திறந்த மூல உரிமங்கள்</string>\n    <string name=\"select_only_offline\">ஆஃப்லைனில் மட்டும் தேர்ந்தெடுக்கவும்</string>\n    <string name=\"maze_settings_auto_scroll_title\">தற்போதைய கேள்விக்கு ஆட்டோ ச்க்ரோல்</string>\n    <string name=\"maze_settings_auto_scroll_summary\">பிரமை விளையாடும்போது தற்போதைய கேள்விக்கு தானாக உருட்டவும்.</string>\n    <string name=\"remaining_time\">மீதமுள்ள: %1$s</string>\n    <string name=\"claim_n_diamonds\">உரிமைகோரல் %1$d</string>\n    <string name=\"regional_preferences\">பிராந்திய விருப்பத்தேர்வுகள்</string>\n    <string name=\"temperature_unit\">வெப்பநிலை அலகு</string>\n    <string name=\"system_default\">கணினி இயல்புநிலை</string>\n    <string name=\"celsius\">செல்சியச்</string>\n    <string name=\"fahrenheit\">பாரன்ஈட்</string>\n    <string name=\"distance_unit\">தூர அலகு</string>\n    <string name=\"metric\">மெட்ரிக்</string>\n    <string name=\"imperial\">ஏகாதிபத்திய</string>\n    <string name=\"no_data\">தரவு இல்லை</string>\n    <string name=\"no_questions_saved_yet\">இதுவரை எந்த கேள்வியும் சேமிக்கப்படவில்லை!</string>\n    <string name=\"no_questions_saved_description\">ஆஃப்லைனில் விளையாடத் தொடங்க கேள்விகளைச் சேமிக்கவும் அல்லது பதிவிறக்கவும்.</string>\n    <string name=\"level_completed\">நிலை முடிந்தது!</string>\n    <string name=\"next_level\">அடுத்த நிலை</string>\n    <string name=\"main_menu\">பட்டியல் விளையாடுங்கள்</string>\n</resources>\n"
  },
  {
    "path": "core/src/main/res/values-v29/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"Theme.NewQuiz\" parent=\"Base.Theme.NewQuiz\">\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">?attr/isLightTheme</item>\n    </style>\n</resources>"
  },
  {
    "path": "core/src/normal/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n    <application>\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            android:exported=\"false\"\n            tools:node=\"merge\">\n            <meta-data\n                android:name=\"com.infinitepower.newquiz.core.initializer.CoreFirebaseInitializer\"\n                android:value=\"androidx.startup\" />\n        </provider>\n    </application>\n</manifest>"
  },
  {
    "path": "core/src/normal/kotlin/com/infinitepower/newquiz/core/initializer/CoreFirebaseInitializer.kt",
    "content": "package com.infinitepower.newquiz.core.initializer\n\nimport android.content.Context\nimport android.util.Log\nimport androidx.startup.Initializer\nimport com.google.firebase.FirebaseApp\nimport com.google.firebase.Firebase\nimport com.google.firebase.initialize\n\nclass CoreFirebaseInitializer : Initializer<FirebaseApp> {\n    private companion object {\n        private const val TAG = \"CoreFirebaseInitializer\"\n    }\n\n    override fun create(context: Context): FirebaseApp {\n        Log.d(TAG, \"Initializing Firebase\")\n        return Firebase.initialize(context) ?: throw IllegalStateException(\"FirebaseApp is null\")\n    }\n\n    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()\n}"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/NumberFormatterTest.kt",
    "content": "package com.infinitepower.newquiz.core\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.NumberFormatter.Distance.DistanceUnit\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.regional_preferences.DistanceUnitType\nimport com.infinitepower.newquiz.model.regional_preferences.RegionalPreferences\nimport com.infinitepower.newquiz.model.regional_preferences.TemperatureUnit\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.CsvSource\nimport java.text.DateFormat\nimport java.text.NumberFormat\nimport java.util.Locale\n\n/**\n * Tests for [NumberFormatter].\n */\ninternal class NumberFormatterTest {\n    private val numberFormat = NumberFormat.getNumberInstance(Locale.getDefault())\n    private val dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())\n    private val timeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault())\n    private val dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())\n\n    // Number type tests\n\n    @Test\n    fun `test Number formatValueToString without helperValueSuffix`() {\n        val number = 12345.6789\n        val expected = numberFormat.format(number)\n\n        val formatter = NumberFormatter.from(NumberFormatType.DEFAULT)\n        assertThat(formatter.formatValueToString(number)).isEqualTo(expected)\n    }\n\n    @Test\n    fun `test Number formatValueToString with helperValueSuffix`() {\n        val number = 12345.6789\n        val suffix = \"m\"\n        val expected = \"${numberFormat.format(number)} $suffix\"\n\n        val formatter = NumberFormatter.from(NumberFormatType.DEFAULT)\n        assertThat(formatter.formatValueToString(number, suffix)).isEqualTo(expected)\n    }\n\n    // Date type tests\n\n    @Test\n    fun `test Date formatValueToString without helperValueSuffix`() {\n        val timestamp = 1627196400000.0 // July 25, 2021\n        val expected = dateFormat.format(timestamp.toLong())\n\n        val formatter = NumberFormatter.from(NumberFormatType.DATE)\n        assertThat(formatter.formatValueToString(timestamp)).isEqualTo(expected)\n    }\n\n    @Test\n    fun `test Date formatValueToString with helperValueSuffix`() {\n        val timestamp = 1627196400000.0 // July 25, 2021\n        val suffix = \"UTC\"\n        val expected = \"${dateFormat.format(timestamp.toLong())} $suffix\"\n\n        val formatter = NumberFormatter.from(NumberFormatType.DATE)\n        assertThat(formatter.formatValueToString(timestamp, suffix)).isEqualTo(expected)\n    }\n\n    // Time type tests\n\n    @Test\n    fun `test Time formatValueToString without helperValueSuffix`() {\n        val timestamp = 1627196400000.0 // July 25, 2021 12:00:00 AM GMT\n        val expected = timeFormat.format(timestamp.toLong())\n\n        val formatter = NumberFormatter.from(NumberFormatType.TIME)\n        assertThat(formatter.formatValueToString(timestamp)).isEqualTo(expected)\n    }\n\n    @Test\n    fun `test Time formatValueToString with helperValueSuffix`() {\n        val timestamp = 1627196400000.0 // July 25, 2021 12:00:00 AM GMT\n        val suffix = \"UTC\"\n        val expected = \"${timeFormat.format(timestamp.toLong())} $suffix\"\n\n        val formatter = NumberFormatter.from(NumberFormatType.TIME)\n        assertThat(formatter.formatValueToString(timestamp, suffix)).isEqualTo(expected)\n    }\n\n    // DateTime type tests\n\n    @Test\n    fun `test DateTime formatValueToString without helperValueSuffix`() {\n        val timestamp = 1627196400000.0 // July 25, 2021 12:00:00 AM GMT\n        val expected = dateTimeFormat.format(timestamp.toLong())\n\n        val formatter = NumberFormatter.from(NumberFormatType.DATETIME)\n        assertThat(formatter.formatValueToString(timestamp)).isEqualTo(expected)\n    }\n\n    @Test\n    fun `test DateTime formatValueToString with helperValueSuffix`() {\n        val timestamp = 1627196400000.0 // July 25, 2021 12:00:00 AM GMT\n        val suffix = \"UTC\"\n        val expected = \"${dateTimeFormat.format(timestamp.toLong())} $suffix\"\n\n        val formatter = NumberFormatter.from(NumberFormatType.DATETIME)\n        assertThat(formatter.formatValueToString(timestamp, suffix)).isEqualTo(expected)\n    }\n\n    // Percentage type tests\n\n    @Test\n    fun `test percentage formatValueToString should format percentage value to string`() {\n        val value = 0.75\n        val expected = \"75%\"\n\n        val formatter = NumberFormatter.from(NumberFormatType.PERCENTAGE)\n        assertThat(formatter.formatValueToString(value)).isEqualTo(expected)\n    }\n\n    @Test\n    fun `test percentage formatValueToString should include helperValueSuffix if not null`() {\n        val value = 0.75\n        val suffix = \"of the time\"\n        val expected = \"75% of the time\"\n\n        val formatter = NumberFormatter.from(NumberFormatType.PERCENTAGE)\n        assertThat(formatter.formatValueToString(value, suffix)).isEqualTo(expected)\n    }\n\n    @Test\n    fun `test percentage formatValueToString should format percentage higher than 100 percent`() {\n        val formatter = NumberFormatter.from(NumberFormatType.PERCENTAGE)\n        assertThat(formatter.formatValueToString(1.3)).isEqualTo(\"130%\")\n    }\n\n    // Temperature tests\n\n    @CsvSource(\n        \"0.0, celsius, de, 0.0 °C\", // German: Celsius to Celsius\n        \"0.0, celsius, us, 32 °F\", // US: Celsius to Fahrenheit\n        \"0.0, fahrenhe, de, -17.78 °C\", // German: Fahrenheit to Celsius\n        \"0.0, fahrenhe, us, 0.0 °F\", // US: Fahrenheit to Fahrenheit\n    )\n    @ParameterizedTest(name = \"{0} in {1} should be formatted to {3} in {2} locale\")\n    fun `test temperature formatValueToString`(\n        valueToFormat: Double,\n        temperatureUnitStr: String,\n        convertCountry: String,\n        expected: String\n    ) {\n        val formatter = NumberFormatter.from(NumberFormatType.TEMPERATURE)\n\n        val locale = Locale(\"en\", convertCountry)\n\n        val formattedValue = formatter.formatValueToString(\n            value = valueToFormat,\n            helperValueSuffix = temperatureUnitStr,\n            regionalPreferences = RegionalPreferences(locale = locale)\n        )\n        assertThat(formattedValue).isEqualTo(expected)\n    }\n\n    @CsvSource(\n        \"0.0, CELSIUS, CELSIUS, 0.0 °C\", // German: Celsius to Celsius\n        \"0.0, CELSIUS, FAHRENHEIT, 32 °F\", // US: Celsius to Fahrenheit\n        \"0.0, FAHRENHEIT, CELSIUS, -17.78 °C\", // German: Fahrenheit to Celsius\n        \"0.0, FAHRENHEIT, FAHRENHEIT, 0.0 °F\", // US: Fahrenheit to Fahrenheit\n    )\n    @ParameterizedTest(name = \"{0} in {1} should be formatted to {3} in {2} locale\")\n    fun `test temperature formatValueToString when regionalPreferences temperatureUnit is configured`(\n        valueToFormat: Double,\n        valueTemperatureUnit: TemperatureUnit,\n        convertTemperatureUnit: TemperatureUnit,\n        expected: String\n    ) {\n        val formatter = NumberFormatter.from(NumberFormatType.TEMPERATURE)\n\n        val formattedValue = formatter.formatValueToString(\n            value = valueToFormat,\n            helperValueSuffix = valueTemperatureUnit.key,\n            regionalPreferences = RegionalPreferences(\n                temperatureUnit = convertTemperatureUnit, // This should override the default locale\n            )\n        )\n        assertThat(formattedValue).isEqualTo(expected)\n    }\n\n    // Distance tests\n\n    @CsvSource(\n        \"1.0, kilometer, de, 1 km\", // German: Meter to Meter\n        \"1.0, kilometer, us, 0.62 mi\", // US: Meter to Mile\n    )\n    @ParameterizedTest(name = \"{0} in {1} should be formatted to {3} in {2} locale\")\n    fun `test distance formatValueToString when regionalPreferences distanceUnit is not configured`(\n        valueToFormat: Double,\n        distanceUnit: String,\n        convertCountry: String,\n        expected: String\n    ) {\n        val formatter = NumberFormatter.from(NumberFormatType.DISTANCE)\n\n        val locale = Locale(\"en\", convertCountry)\n\n        val formattedValue = formatter.formatValueToString(\n            value = valueToFormat,\n            helperValueSuffix = distanceUnit,\n            regionalPreferences = RegionalPreferences(\n                locale = locale,\n            )\n        )\n        assertThat(formattedValue).isEqualTo(expected)\n    }\n\n    @CsvSource(\n        \"1.0, KILOMETER, METRIC, 1 km\", // Kilometer to Kilometer (Metric) system\n        \"1.0, KILOMETER, IMPERIAL, 0.62 mi\", // Kilometer to Mile (Imperial) system\n        \"1.0, METER, METRIC, 1 m\", // Meter to Meter (Metric) system\n        \"1.0, METER, IMPERIAL, 3.28 ft\", // Meter to Foot (Imperial) system\n        \"1.0, MILE, METRIC, 1.61 km\", // Mile to Kilometer (Metric) system\n        \"1.0, MILE, IMPERIAL, 1 mi\", // Mile to Mile (Imperial) system\n        \"1.0, FOOT, METRIC, 0.3 m\", // Foot to Meter (Metric) system\n        \"1.0, FOOT, IMPERIAL, 1 ft\", // Foot to Foot (Imperial) system\n    )\n    @ParameterizedTest(name = \"{0} in {1} should be formatted to {3} in {2} locale\")\n    fun `test distance formatValueToString when regionalPreferences distanceUnit is configured`(\n        valueToFormat: Double,\n        valueDistanceUnit: DistanceUnit,\n        convertDistanceUnitType: DistanceUnitType,\n        expected: String\n    ) {\n        val formatter = NumberFormatter.from(NumberFormatType.DISTANCE)\n\n        val formattedValue = formatter.formatValueToString(\n            value = valueToFormat,\n            helperValueSuffix = valueDistanceUnit.key,\n            regionalPreferences = RegionalPreferences(\n                distanceUnitType = convertDistanceUnitType, // This should override the default locale\n            )\n        )\n        assertThat(formattedValue).isEqualTo(expected)\n    }\n}\n"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/game/ComparisonQuizDataTest.kt",
    "content": "package com.infinitepower.newquiz.core.game\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.data.fake.FakeComparisonQuizData\nimport com.infinitepower.newquiz.core.testing.utils.mockAndroidLog\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport java.net.URI\nimport kotlin.test.BeforeTest\n\nclass ComparisonQuizDataTest {\n    @BeforeTest\n    fun setUp() {\n        mockAndroidLog()\n    }\n\n    @Test\n    fun `nextQuestion should return a new ComparisonQuizData object with a new current question`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = URI(\"\"),\n            value = 10.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = URI(\"\"),\n            value = 5.0\n        )\n\n        val quizData = ComparisonQuizCore.QuizData(\n            questions = listOf(quizItem1, quizItem2),\n            currentQuestion = null,\n            questionDescription = \"Which country has more population?\",\n            comparisonMode = ComparisonMode.GREATER,\n            category = FakeComparisonQuizData.generateCategory()\n        )\n\n        assertThat(quizData.questions).containsExactly(quizItem1, quizItem2)\n        assertThat(quizData.currentQuestion).isNull()\n\n        val nextQuizData = quizData.getNextQuestion()\n\n        assertThat(nextQuizData.questions).containsNoneOf(quizItem1, quizItem2)\n        assertThat(nextQuizData.questions).isEmpty()\n        assertThat(nextQuizData.currentQuestion).isNotNull()\n    }\n\n    @Test\n    fun `nextQuestion should return throw IllegalStateException when questions list is empty`() {\n        val quizData = ComparisonQuizCore.QuizData(\n            questions = emptyList(),\n            currentQuestion = null,\n            questionDescription = \"Which country has more population?\",\n            comparisonMode = ComparisonMode.GREATER,\n            category = FakeComparisonQuizData.generateCategory()\n        )\n\n        assertThat(quizData.questions).isEmpty()\n        assertThat(quizData.currentQuestion).isNull()\n\n        val exception = assertThrows<GameOverException> {\n            quizData.getNextQuestion()\n        }\n\n        assertThat(exception).hasMessageThat().isEqualTo(\"Questions list is empty\")\n    }\n\n    @Test\n    fun `test nextQuestion when questions has size of 3`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = URI(\"\"),\n            value = 10.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = URI(\"\"),\n            value = 5.0\n        )\n\n        val quizItem3 = ComparisonQuizItem(\n            title = \"C\",\n            imgUri = URI(\"\"),\n            value = 8.0\n        )\n\n        val quizData = ComparisonQuizCore.QuizData(\n            questions = listOf(quizItem1, quizItem2, quizItem3),\n            currentQuestion = null,\n            questionDescription = \"Which country has more population?\",\n            comparisonMode = ComparisonMode.GREATER,\n            category = FakeComparisonQuizData.generateCategory()\n        )\n\n        assertThat(quizData.questions).containsExactly(quizItem1, quizItem2, quizItem3)\n        assertThat(quizData.currentQuestion).isNull()\n\n        val nextQuizData = quizData.getNextQuestion()\n\n        assertThat(nextQuizData.questions).containsExactly(quizItem3)\n        assertThat(nextQuizData.questions).containsNoneOf(quizItem1, quizItem2)\n        assertThat(nextQuizData.currentQuestion).isNotNull()\n    }\n\n    @Test\n    fun `test nextQuestion when questions has size of 1 and current question is not null`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = URI(\"\"),\n            value = 10.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = URI(\"\"),\n            value = 5.0\n        )\n\n        val quizItem3 = ComparisonQuizItem(\n            title = \"C\",\n            imgUri = URI(\"\"),\n            value = 8.0\n        )\n\n        val quizData = ComparisonQuizCore.QuizData(\n            questions = listOf(quizItem3),\n            currentQuestion = ComparisonQuizQuestion(\n                questions = quizItem1 to quizItem2,\n                categoryId = \"\",\n                comparisonMode = ComparisonMode.GREATER\n            ),\n            questionDescription = \"Which country has more population?\",\n            comparisonMode = ComparisonMode.GREATER,\n            category = FakeComparisonQuizData.generateCategory()\n        )\n\n        assertThat(quizData.questions).containsExactly(quizItem3)\n        assertThat(quizData.currentQuestion).isNotNull()\n        assertThat(quizData.currentQuestion?.questions?.first).isEqualTo(quizItem1)\n        assertThat(quizData.currentQuestion?.questions?.second).isEqualTo(quizItem2)\n\n        val nextQuizData = quizData.getNextQuestion()\n\n        assertThat(nextQuizData.questions).isEmpty()\n        assertThat(nextQuizData.questions).containsNoneOf(quizItem1, quizItem2, quizItem3)\n        assertThat(nextQuizData.currentQuestion).isNotNull()\n        assertThat(nextQuizData.currentQuestion?.questions?.first).isEqualTo(quizItem2)\n        assertThat(nextQuizData.currentQuestion?.questions?.second).isEqualTo(quizItem3)\n    }\n}"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/util/UiTextTests.kt",
    "content": "package com.infinitepower.newquiz.core.util\n\nimport android.content.Context\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.toUiText\nimport io.mockk.mockk\nimport org.junit.jupiter.api.Test\n\n/**\n * Tests for [UiText].\n */\ninternal class UiTextTests {\n    @Test\n    fun `String#toUiText() should return a UiText`() {\n        val uiText = \"Play %d Questions\".toUiText(5)\n\n        val context = mockk<Context>()\n\n        assertThat(uiText).isInstanceOf(UiText.DynamicString::class.java)\n        assertThat(uiText.asString(context)).isEqualTo(\"Play 5 Questions\")\n    }\n\n    @Test\n    fun `String#toUiText() should return a UiText with multiple arguments`() {\n        val uiText = \"Play %d Questions in %s\".toUiText(5, \"English\")\n\n        val context = mockk<Context>()\n\n        assertThat(uiText).isInstanceOf(UiText.DynamicString::class.java)\n        assertThat(uiText.asString(context)).isEqualTo(\"Play 5 Questions in English\")\n    }\n\n    @Test\n    fun `String#toUiText() should return a UiText with no arguments`() {\n        val uiText = \"Play Questions\".toUiText()\n\n        val context = mockk<Context>()\n\n        assertThat(uiText).isInstanceOf(UiText.DynamicString::class.java)\n        assertThat(uiText.asString(context)).isEqualTo(\"Play Questions\")\n    }\n}"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/util/collections/CollectionsTest.kt",
    "content": "package com.infinitepower.newquiz.core.util.collections\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.Test\n\nclass IndexOfFirstOrNullTest {\n    @Test\n    @DisplayName(\"Should return the index of the first element that satisfies the predicate\")\n    fun indexOfFirstOrNullWhenPredicateIsSatisfiedThenReturnTheIndex() {\n        val list = listOf(1, 2, 3, 4, 5)\n        val result = list.indexOfFirstOrNull { it == 2 }\n\n        assertThat(result).isEqualTo(1)\n    }\n\n    @Test\n    @DisplayName(\"Should return null when the predicate is not satisfied\")\n    fun indexOfFirstOrNullWhenPredicateIsNotSatisfiedThenReturnNull() {\n        val list = listOf(1, 2, 3, 4, 5)\n        val result = list.indexOfFirstOrNull { it == 6 }\n\n        assertThat(result).isNull()\n    }\n}"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/util/kotlin/BooleanUtilsTest.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.Test\n\nclass BooleanExtensionsTest {\n    @Test\n    @DisplayName(\"Should return 1 when the boolean is true\")\n    fun toIntWhenBooleanIsTrue() {\n        val boolean = true\n        val expected = 1\n        val actual = boolean.toInt()\n        assertThat(expected).isEqualTo(actual)\n    }\n\n    @Test\n    @DisplayName(\"Should return 0 when the boolean is false\")\n    fun toIntWhenBooleanIsFalse() {\n        val boolean = false\n        val expected = 0\n        val actual = boolean.toInt()\n        assertThat(expected).isEqualTo(actual)\n    }\n\n    @Test\n    @DisplayName(\"Should return 1L when the boolean is true\")\n    fun toLongWhenBooleanIsTrue() {\n        val boolean = true\n        val expected = 1L\n        val actual = boolean.toLong()\n        assertThat(expected).isEqualTo(actual)\n    }\n\n    @Test\n    @DisplayName(\"Should return 0L when the boolean is false\")\n    fun toLongWhenBooleanIsFalse() {\n        val boolean = false\n        val expected = 0L\n        val actual = boolean.toLong()\n        assertThat(expected).isEqualTo(actual)\n    }\n}"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/util/kotlin/CollectionsUtilsTest.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nimport com.google.common.truth.Truth.assertThat\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.Test\nimport kotlin.random.Random\n\n@OptIn(ExperimentalCoroutinesApi::class)\ninternal class CollectionUtilsTest {\n    @Test\n    fun `sum of empty intRanges returns 0 to 0`() {\n        val intRanges = emptyList<IntRange>()\n        val sum = intRanges.sum()\n\n        assertThat(sum).isEqualTo(0..0)\n    }\n\n    @Test\n    @DisplayName(\"Should return the sum of two ranges\")\n    fun sumOfTwoRanges() {\n        val range1 = 1..10\n        val range2 = 11..20\n\n        val expected = 12..30\n\n        val result = listOf(range1, range2).sum()\n\n        assertThat(result).isEqualTo(expected)\n    }\n\n    @Test\n    @DisplayName(\"Should return the sum of three ranges\")\n    fun sumOfThreeRanges() {\n        val range1 = 1..10\n        val range2 = 11..20\n        val range3 = 21..30\n\n        val result = listOf(range1, range2, range3).sum()\n\n        assertThat(result).isEqualTo(33..60)\n    }\n\n    @Test\n    fun testGenerateRandomUniqueItems_correctSize() = runTest  {\n        val questionSize = 5\n        val generator: () -> Int = { Random.nextInt() }\n\n        val items = generateRandomUniqueItems(questionSize, generator)\n\n        assertThat(items).hasSize(questionSize)\n        assertThat(items).containsNoDuplicates()\n    }\n\n    @Test\n    fun testGenerateRandomUniqueItems_generatedItems() = runTest  {\n        val questionSize = 5\n        val expectedItems = listOf(1, 2, 3, 4, 5)\n        var index = 0\n        val generator: () -> Int = {\n            val item = expectedItems[index]\n            index++\n            item\n        }\n\n        val items = generateRandomUniqueItems(questionSize, generator)\n\n        assertThat(items).containsExactlyElementsIn(expectedItems)\n    }\n\n    @Test\n    fun testGenerateRandomUniqueItems_sameGenerated_returnsOneSize() = runTest {\n        val questionSize = 5\n        val generator: () -> Int = { 0 }\n\n        val items = generateRandomUniqueItems(questionSize, generator, 1000)\n\n        assertThat(items).hasSize(1)\n        assertThat(items).containsNoDuplicates()\n    }\n\n    @Test\n    fun `Test ClosedFloatingPointRange increaseEndBy`() {\n        val range = 1f..3f\n        val result = range increaseEndBy 2f\n        assertThat(result).isEqualTo(3f..5f)\n    }\n\n    @Test\n    fun `test generateIncorrectNumberAnswers`() = runTest {\n        val mockRandom = mockk<Random>()\n\n        every { mockRandom.nextInt(0, 20) } returnsMany listOf(11, 15, 13, 8)\n\n        val result = generateIncorrectNumberAnswers(\n            answerCount = 3,\n            correctSolution = 10,\n            fromRange = 10,\n            toRange = 10,\n            random = mockRandom\n        )\n\n        assertThat(result).containsExactly(11, 15, 13)\n    }\n\n    @Test\n    fun `test generateIncorrectNumberAnswers from 0 to 20`() = runTest {\n        val mockRandom = mockk<Random>()\n\n        every { mockRandom.nextInt(10, 30) } returnsMany listOf(23, 11, 14, 28)\n\n        val result = generateIncorrectNumberAnswers(\n            answerCount = 3,\n            correctSolution = 10,\n            fromRange = 0,\n            toRange = 20,\n            random = mockRandom\n        )\n\n        assertThat(result).containsExactly(23, 11, 14)\n    }\n}"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/util/kotlin/MathTest.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.CsvSource\nimport kotlin.random.Random\nimport kotlin.random.nextULong\nimport kotlin.test.Test\n\ninternal class MathTest {\n    @ParameterizedTest(name = \"roundToUInt({0}) = {1}\")\n    @CsvSource(\"0.0, 0\", \"0.1, 0\", \"0.5, 1\", \"0.9, 1\", \"1.0, 1\", \"-1.0, -1\")\n    fun testRoundToUInt(value: Double, expected: UInt) {\n        assertThat(value.roundToUInt()).isEqualTo(expected)\n    }\n\n    @ParameterizedTest(name = \"{0} pow {1} = {2}\")\n    @CsvSource(\"0, 0, 1\", \"0, 1, 0\", \"1, 0, 1\", \"1, 1, 1\", \"2, 2, 4\", \"2, 3, 8\", \"2, 4, 16\")\n    fun testUIntPow(value: UInt, n: Int, expected: UInt) {\n        assertThat(value pow n).isEqualTo(expected)\n    }\n\n    @Test\n    fun `test ULong div Float`() {\n        val randomDividend = Random.nextULong()\n        val randomDivisor = Random.nextFloat()\n\n        val expected = randomDividend.toLong() / randomDivisor\n\n        assertThat(randomDividend / randomDivisor).isEqualTo(expected)\n    }\n\n    @Test\n    fun `test ULong div Double`() {\n        val randomDividend = Random.nextULong()\n        val randomDivisor = Random.nextDouble()\n\n        val expected = randomDividend.toLong() / randomDivisor\n        assertThat(randomDividend / randomDivisor).isEqualTo(expected)\n    }\n}\n"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/util/kotlin/NumberUtilsTest.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nimport com.google.common.truth.Truth.assertThat\nimport kotlin.test.Test\n\ninternal class NumberUtilsTest {\n    @Test\n    fun `converts single digit to double digit`() {\n        val digit = 5\n        val result = digit.toDoubleDigit()\n        assertThat(result).isEqualTo(\"05\")\n    }\n\n    @Test\n    fun `does not convert double digit`() {\n        val digit = 15\n        val result = digit.toDoubleDigit()\n        assertThat(result).isEqualTo(\"15\")\n    }\n\n    @Test\n    fun `multiplies uInt by float`() {\n        val uInt = 10u\n        val multiplierFactor = 1.5f\n        val result = uInt * multiplierFactor\n        assertThat(result).isEqualTo(15u)\n    }\n\n    @Test\n    fun `multiplies uIntRange by float`() {\n        val uIntRange = 10u..20u\n        val multiplierFactor = 1.5f\n        val result = uIntRange * multiplierFactor\n        assertThat(result).isEqualTo(15u..30u)\n    }\n}"
  },
  {
    "path": "core/src/test/java/com/infinitepower/newquiz/core/util/kotlin/SetUtilsTest.kt",
    "content": "package com.infinitepower.newquiz.core.util.kotlin\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.api.Test\n\ninternal class SetUtilsTest {\n    @Test\n    fun `Test Set removeFirst`() {\n        val set = setOf(\"apple\", \"banana\", \"cherry\")\n        val result = set.removeFirst()\n        assertThat(result).containsExactly(\"banana\", \"cherry\")\n    }\n\n    @Test\n    fun `Test Set removeLast`() {\n        val set = setOf(\"apple\", \"banana\", \"cherry\")\n        val result = set.removeLast()\n        assertThat(result).containsExactly(\"apple\", \"banana\")\n    }\n}"
  },
  {
    "path": "core/testing/.gitignore",
    "content": "/build"
  },
  {
    "path": "core/testing/README.md",
    "content": "# Core Testing Module (:core:testing)\n\nThis module provides a set of utilities to help with testing.\n"
  },
  {
    "path": "core/testing/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library.compose)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.core.testing\"\n}\n\ndependencies {\n    api(libs.androidx.activity.compose)\n    api(libs.androidx.compose.ui.test)\n    api(libs.androidx.test.rules)\n    api(libs.androidx.test.runner)\n    api(libs.hilt.android.testing)\n    api(libs.kotlinx.coroutines.test)\n    api(libs.androidx.compose.material3)\n\n    api(libs.ktor.client.mock)\n\n    api(libs.room.testing)\n    api(libs.androidx.work.testing)\n\n    debugApi(libs.androidx.compose.ui.testManifest)\n\n    implementation(libs.kotlinx.datetime)\n\n    api(libs.mockk)\n\n    implementation(projects.model)\n    implementation(projects.core)\n    implementation(projects.core.analytics)\n    api(projects.core.database)\n    api(projects.core.datastore)\n    implementation(projects.core.remoteConfig)\n    implementation(projects.data)\n    implementation(projects.domain)\n}\n"
  },
  {
    "path": "core/testing/src/foss/kotlin/com/infinitepower/newquiz/core/testing/di/TestRemoteConfigModule.kt",
    "content": "package com.infinitepower.newquiz.core.testing.di\n\nimport android.content.Context\nimport com.infinitepower.newquiz.core.remote_config.LocalDefaultsRemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.initializer.RemoteConfigInitializer\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport dagger.hilt.testing.TestInstallIn\nimport javax.inject.Singleton\n\n@Module\n@TestInstallIn(\n    components = [SingletonComponent::class],\n    replaces = [RemoteConfigInitializer::class]\n)\nobject TestRemoteConfigModule {\n    @Provides\n    @Singleton\n    fun provideRemoteConfig(\n        @ApplicationContext context: Context\n    ): RemoteConfig = LocalDefaultsRemoteConfig(context).also {\n        it.initialize()\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/NewQuizTestRunner.kt",
    "content": "package com.infinitepower.newquiz.core.testing\n\nimport android.app.Application\nimport android.content.Context\nimport androidx.test.runner.AndroidJUnitRunner\nimport dagger.hilt.android.testing.HiltTestApplication\n\nclass NewQuizTestRunner : AndroidJUnitRunner() {\n    override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {\n        return super.newApplication(cl, HiltTestApplication::class.java.name, context)\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/ScreenshotComparator.kt",
    "content": "package com.infinitepower.newquiz.core.testing\n\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.os.Build\nimport androidx.annotation.RequiresApi\nimport androidx.compose.ui.graphics.asAndroidBitmap\nimport androidx.compose.ui.test.SemanticsNodeInteraction\nimport androidx.compose.ui.test.captureToImage\nimport androidx.test.platform.app.InstrumentationRegistry\nimport java.io.File\nimport java.io.FileOutputStream\n\n/**\n * Simple on-device screenshot comparator that uses golden images present in\n * `androidTest/assets`,\n *\n * Minimum SDK is O. Densities between devices must match.\n *\n * Screenshots are saved on device in `/data/data/{package}/files`.\n */\n@RequiresApi(Build.VERSION_CODES.O)\nfun SemanticsNodeInteraction.assertMatchesGolden(\n    goldenName: String,\n    folderPath: String? = null\n) {\n    val bitmap = captureToImage().asAndroidBitmap()\n\n    // Save screenshot to file for debugging\n    saveScreenshot(goldenName + System.currentTimeMillis().toString(), bitmap)\n\n    val fileName = if (folderPath == null) \"$goldenName.png\" else \"$folderPath/$goldenName.png\"\n\n    val golden = InstrumentationRegistry\n        .getInstrumentation()\n        .context\n        .resources\n        .assets\n        .open(fileName)\n        .use { BitmapFactory.decodeStream(it) }\n\n    golden.compare(bitmap)\n}\n\nprivate const val SCREENSHOT_QUALITY = 100\n\nprivate fun saveScreenshot(filename: String, bmp: Bitmap) {\n    val path = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.canonicalPath\n    FileOutputStream(\"$path/$filename.png\").use { out ->\n        bmp.compress(Bitmap.CompressFormat.PNG, SCREENSHOT_QUALITY, out)\n    }\n    println(\"Saved screenshot to $path/$filename.png\")\n}\n\nprivate fun Bitmap.compare(other: Bitmap) {\n    if (this.width != other.width || this.height != other.height) {\n        throw AssertionError(\"Size of screenshot does not match golden file (check device density)\")\n    }\n    // Compare row by row to save memory on device\n    val row1 = IntArray(width)\n    val row2 = IntArray(width)\n    for (column in 0 until height) {\n        // Read one row per bitmap and compare\n        this.getRow(row1, column)\n        other.getRow(row2, column)\n        if (!row1.contentEquals(row2)) {\n            throw AssertionError(\"Sizes match but bitmap content has differences\")\n        }\n    }\n}\n\nprivate fun Bitmap.getRow(pixels: IntArray, column: Int) {\n    this.getPixels(pixels, 0, width, 0, column, width, 1)\n}\n\nfun clearExistingImages(folderName: String) {\n    val path = File(InstrumentationRegistry.getInstrumentation().targetContext.filesDir, folderName)\n    path.deleteRecursively()\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/data/fake/FakeComparisonQuizData.kt",
    "content": "package com.infinitepower.newquiz.core.testing.data.fake\n\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\nimport java.net.URI\nimport kotlin.random.Random\n\nobject FakeComparisonQuizData {\n    fun generateCategories(count: Int = 10): List<ComparisonQuizCategory> {\n        return List(count, ::generateCategory)\n    }\n\n    fun generateCategory(id: Int = 1): ComparisonQuizCategory = ComparisonQuizCategory(\n        id = id.toString(),\n        name = UiText.DynamicString(\"Category $id\"),\n        image = \"image_url_$id\",\n        description = \"Description $id\",\n        questionDescription = ComparisonQuizCategory.QuestionDescription(\n            greater = \"Greater $id\",\n            less = \"Less $id\"\n        ),\n        formatType = NumberFormatType.DEFAULT,\n    )\n\n    fun generateQuestion(\n        categoryId: String = \"1\",\n        comparisonMode: ComparisonMode = ComparisonMode.GREATER\n    ): ComparisonQuizQuestion {\n        val question1 = generateQuestionItem()\n        val question2 = generateQuestionItem()\n\n        return ComparisonQuizQuestion(\n            questions = question1 to question2,\n            categoryId = categoryId,\n            comparisonMode = comparisonMode\n        )\n    }\n\n    fun generateQuestionItem(): ComparisonQuizItem = ComparisonQuizItem(\n        title = \"Title\",\n        value = Random.nextDouble(),\n        imgUri = URI.create(\"\")\n    )\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/data/fake/FakeData.kt",
    "content": "package com.infinitepower.newquiz.core.testing.data.fake\n\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport kotlinx.datetime.Clock\nimport kotlinx.datetime.Instant\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.days\n\nobject FakeData {\n    fun generateTasks(\n        count: Int = 10,\n        dayDuration: ClosedRange<Duration> = 0.days..1.days\n    ): List<DailyChallengeTask> {\n        val now = Clock.System.now()\n        val dateRange = (now + dayDuration.start)..(now + dayDuration.endInclusive)\n\n        return List(count) { id -> generateTask(id + 1, dateRange) }\n    }\n\n    fun generateTask(id: Int, dateRange: ClosedRange<Instant>): DailyChallengeTask {\n        return DailyChallengeTask(\n            id = id,\n            title = UiText.DynamicString(\"Task $id\"),\n            diamondsReward = 10u,\n            experienceReward = 10u,\n            isClaimed = false,\n            dateRange = dateRange,\n            currentValue = 0u,\n            maxValue = 10u,\n            event = GameEvent.MultiChoice.PlayRandomQuiz\n        )\n    }\n\n    fun generateTasksWithOffset(\n        size: Int = 10,\n        instant: Instant = Clock.System.now(),\n        offset: Duration = DEFAULT_TASKS_OFFSET.days // Offset to ensure tasks are in the 4 days in the past\n    ): List<DailyChallengeTask> {\n        return List(size) { day ->\n            val startDate = instant + day.days + offset\n            val endDate = instant + (day + 1).days + offset\n            val dateRange = startDate..endDate\n\n            generateTask(id = day + 1, dateRange)\n        }\n    }\n}\n\nprivate const val DEFAULT_TASKS_OFFSET = -4\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/data/repository/comparison_quiz/FakeComparisonQuizRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.core.testing.data.repository.comparison_quiz\n\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.toUiText\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.map\nimport java.net.URI\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\nprivate const val CATEGORIES_TO_GENERATE = 1\n\n@Singleton\nclass FakeComparisonQuizRepositoryImpl @Inject constructor() : ComparisonQuizRepository {\n    private val highestPosition = MutableStateFlow<Map<String, Int>>(emptyMap())\n\n    override fun getCategories(): List<ComparisonQuizCategory> {\n        return List(CATEGORIES_TO_GENERATE) { id ->\n            ComparisonQuizCategory(\n                id = id.toString(),\n                name = \"Category $id\".toUiText(),\n                description = \"Description $id\",\n                image = \"\",\n                questionDescription = ComparisonQuizCategory.QuestionDescription(\n                    greater = \"Greater\",\n                    less = \"Less\"\n                ),\n                formatType = NumberFormatType.DEFAULT\n            )\n        }\n    }\n\n    override fun getCategoryById(id: String): ComparisonQuizCategory? {\n        return getCategories().find { it.id == id }\n    }\n\n    override suspend fun getQuestions(\n        category: ComparisonQuizCategory,\n        size: Int,\n        random: Random\n    ): List<ComparisonQuizItem> = List(size) { id ->\n        ComparisonQuizItem(\n            title = \"Question $id\",\n            value = random.nextDouble(),\n            imgUri = URI(\"\")\n        )\n    }\n\n    override suspend fun getHighestPosition(categoryId: String): Int {\n        return highestPosition.value.getOrDefault(categoryId, 0)\n    }\n\n    override fun getHighestPositionFlow(categoryId: String): Flow<Int> {\n        return highestPosition.map { it.getOrDefault(categoryId, 0)}\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/data/repository/multi_choice_quiz/TestMultiChoiceQuestionRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.core.testing.data.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.MultiChoiceQuestionRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass TestMultiChoiceQuestionRepositoryImpl @Inject constructor() : MultiChoiceQuestionRepository {\n    companion object {\n        private const val ANSWER_COUNT = 4\n    }\n\n    override suspend fun getRandomQuestions(\n        amount: Int,\n        category: MultiChoiceBaseCategory.Normal,\n        difficulty: String?,\n        random: Random\n    ): List<MultiChoiceQuestion> = List(amount) {\n        val questionDifficulty = difficulty?.let { difficultyStr ->\n            QuestionDifficulty.from(difficultyStr)\n        } ?: QuestionDifficulty.random(random)\n\n        val answers =  List(ANSWER_COUNT) { answerNum ->\n            \"Answer $answerNum\"\n        }\n\n        MultiChoiceQuestion(\n            description = \"Question $it\",\n            answers = answers,\n            lang = QuestionLanguage.EN,\n            category = category,\n            correctAns = random.nextInt(ANSWER_COUNT),\n            type = MultiChoiceQuestionType.MULTIPLE,\n            difficulty = questionDifficulty\n        )\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/data/repository/numbers/FakeNumberTriviaQuestionApiImpl.kt",
    "content": "package com.infinitepower.newquiz.core.testing.data.repository.numbers\n\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionApi\nimport com.infinitepower.newquiz.model.number.NumberTriviaQuestionEntity\nimport com.infinitepower.newquiz.model.number.NumberTriviaQuestionsEntity\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass FakeNumberTriviaQuestionApiImpl @Inject constructor() : NumberTriviaQuestionApi {\n    override suspend fun getRandomQuestion(\n        size: Int,\n        minNumber: Int,\n        maxNumber: Int\n    ): NumberTriviaQuestionsEntity {\n        val questions = List(size) {\n            val randomNumber = (minNumber..maxNumber).random()\n\n            NumberTriviaQuestionEntity(\n                number = randomNumber,\n                question = \"What is $randomNumber?\"\n            )\n        }\n\n        return NumberTriviaQuestionsEntity(questions = questions)\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/di/TestDatabaseModule.kt",
    "content": "package com.infinitepower.newquiz.core.testing.di\n\nimport android.content.Context\nimport androidx.room.Room\nimport com.infinitepower.newquiz.core.database.AppDatabase\nimport com.infinitepower.newquiz.core.database.di.DatabaseModule\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport dagger.hilt.testing.TestInstallIn\nimport javax.inject.Singleton\n\n@Module\n@TestInstallIn(\n    components = [SingletonComponent::class],\n    replaces = [DatabaseModule::class]\n)\nobject TestDatabaseModule {\n    @Provides\n    @Singleton\n    fun provideAppDatabase(\n        @ApplicationContext applicationContext: Context\n    ): AppDatabase = Room.inMemoryDatabaseBuilder(\n        applicationContext,\n        AppDatabase::class.java\n    ).build()\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/di/TestKtorModule.kt",
    "content": "package com.infinitepower.newquiz.core.testing.di\n\nimport com.infinitepower.newquiz.core.common.BaseApiUrls\nimport com.infinitepower.newquiz.core.di.KtorModule\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.components.SingletonComponent\nimport dagger.hilt.testing.TestInstallIn\nimport io.ktor.client.HttpClient\nimport io.ktor.client.engine.mock.MockEngine\nimport io.ktor.client.engine.mock.respond\nimport io.ktor.http.headersOf\nimport javax.inject.Singleton\n\n@Module\n@TestInstallIn(\n    components = [SingletonComponent::class],\n    replaces = [KtorModule::class]\n)\nobject TestKtorModule {\n    @Provides\n    @Singleton\n    fun providerKtorClient(): HttpClient = HttpClient(MockEngine) {\n        engine {\n            addHandler { request ->\n                when (request.url.encodedPath) {\n                    \"${BaseApiUrls.NEWQUIZ}/api/comparisonquiz/1\" -> {\n                        respond(\n                            content = \"\"\"\n                                [\n                                    {\n                                        \"title\": \"title1\",\n                                        \"value\": 1.0,\n                                        \"imgUrl\": \"imgUrl1\"\n                                    },\n                                    {\n                                        \"title\": \"title2\",\n                                        \"value\": 2.0,\n                                        \"imgUrl\": \"imgUrl2\"\n                                    }\n                                ]\n                            \"\"\".trimIndent(),\n                            headers = headersOf(\"Content-Type\", \"application/json\")\n                        )\n                    }\n\n                    else -> error(\"Unhandled ${request.url.encodedPath}\")\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/di/TestRepositoryModule.kt",
    "content": "package com.infinitepower.newquiz.core.testing.di\n\nimport com.infinitepower.newquiz.core.testing.data.repository.comparison_quiz.FakeComparisonQuizRepositoryImpl\nimport com.infinitepower.newquiz.core.testing.data.repository.multi_choice_quiz.TestMultiChoiceQuestionRepositoryImpl\nimport com.infinitepower.newquiz.core.testing.data.repository.numbers.FakeNumberTriviaQuestionApiImpl\nimport com.infinitepower.newquiz.data.di.RepositoryModule\nimport com.infinitepower.newquiz.data.repository.comparison_quiz.ComparisonQuizApi\nimport com.infinitepower.newquiz.data.repository.comparison_quiz.ComparisonQuizApiImpl\nimport com.infinitepower.newquiz.data.repository.country.CountryRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.daily_challenge.DailyChallengeRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.home.RecentCategoriesRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.math_quiz.MathQuizCoreRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.maze_quiz.MazeQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.CountryCapitalFlagsQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.FlagQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.GuessMathSolutionRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.LogoQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.numbers.NumberTriviaQuestionRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.wordle.WordleRepositoryImpl\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.CountryCapitalFlagsQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.FlagQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.GuessMathSolutionRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.LogoQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.MultiChoiceQuestionRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionApi\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionRepository\nimport com.infinitepower.newquiz.domain.repository.wordle.WordleRepository\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.components.SingletonComponent\nimport dagger.hilt.testing.TestInstallIn\n\n@Module\n@TestInstallIn(\n    components = [SingletonComponent::class],\n    replaces = [RepositoryModule::class]\n)\nabstract class TestRepositoryModule {\n    @Binds\n    abstract fun bindMultiChoiceQuestionRepository(impl: TestMultiChoiceQuestionRepositoryImpl): MultiChoiceQuestionRepository\n\n    @Binds\n    abstract fun bindSavedMultiChoiceQuestionsRepository(impl: SavedMultiChoiceQuestionsRepositoryImpl): SavedMultiChoiceQuestionsRepository\n\n    @Binds\n    abstract fun bindWordleRepository(impl: WordleRepositoryImpl): WordleRepository\n\n    @Binds\n    abstract fun bindFlagQuizRepository(impl: FlagQuizRepositoryImpl): FlagQuizRepository\n\n    @Binds\n    abstract fun bindLogoQuizRepository(impl: LogoQuizRepositoryImpl): LogoQuizRepository\n\n    @Binds\n    abstract fun bindMathQuizCoreRepository(impl: MathQuizCoreRepositoryImpl): MathQuizCoreRepository\n\n    @Binds\n    abstract fun bindMazeMathQuizRepository(impl: MazeQuizRepositoryImpl): MazeQuizRepository\n\n    @Binds\n    abstract fun bindGuessMathSolutionRepository(impl: GuessMathSolutionRepositoryImpl): GuessMathSolutionRepository\n\n    @Binds\n    abstract fun bindNumberTriviaQuestionApi(impl: FakeNumberTriviaQuestionApiImpl): NumberTriviaQuestionApi\n\n    @Binds\n    abstract fun bindNumberTriviaQuestionRepository(impl: NumberTriviaQuestionRepositoryImpl): NumberTriviaQuestionRepository\n\n    @Binds\n    abstract fun bindCountryCapitalFlagsQuizRepository(impl: CountryCapitalFlagsQuizRepositoryImpl): CountryCapitalFlagsQuizRepository\n\n    @Binds\n    abstract fun bindComparisonQuizRepository(impl: FakeComparisonQuizRepositoryImpl): ComparisonQuizRepository\n\n    @Binds\n    abstract fun bindDailyChallengeRepository(impl: DailyChallengeRepositoryImpl): DailyChallengeRepository\n\n    @Binds\n    abstract fun bindRecentCategoriesRepository(impl: RecentCategoriesRepositoryImpl): RecentCategoriesRepository\n\n    @Binds\n    abstract fun bindCountryRepository(impl: CountryRepositoryImpl): CountryRepository\n\n    @Binds\n    abstract fun bindComparisonQuizApi(impl: ComparisonQuizApiImpl): ComparisonQuizApi\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/di/WorkManagerModule.kt",
    "content": "package com.infinitepower.newquiz.core.testing.di\n\nimport android.content.Context\nimport androidx.work.WorkManager\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject WorkManagerModule {\n    @Singleton\n    @Provides\n    fun provideWorkManager(\n        @ApplicationContext context: Context\n    ): WorkManager = WorkManager.getInstance(context)\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/domain/FakeDailyChallengeDao.kt",
    "content": "package com.infinitepower.newquiz.core.testing.domain\n\nimport com.infinitepower.newquiz.core.database.dao.DailyChallengeDao\nimport com.infinitepower.newquiz.core.database.model.DailyChallengeTaskEntity\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.flow.update\n\nclass FakeDailyChallengeDao : DailyChallengeDao {\n    private val savedTasks = MutableStateFlow<List<DailyChallengeTaskEntity>>(emptyList())\n\n    override fun getAllTasksFlow(): Flow<List<DailyChallengeTaskEntity>> = savedTasks\n\n    override suspend fun getAllTasks(): List<DailyChallengeTaskEntity> {\n        return savedTasks.first()\n    }\n\n    override suspend fun getAvailableTasks(currentTime: Long): List<DailyChallengeTaskEntity> {\n        return savedTasks.first()\n            .filter { task -> task.startDate <= currentTime && task.endDate >= currentTime }\n    }\n\n    override suspend fun tasksAreAvailable(currentTime: Long): Boolean {\n        return savedTasks.first()\n            .any { task -> task.startDate <= currentTime && task.endDate >= currentTime }\n    }\n\n    override suspend fun getTaskByType(type: String): DailyChallengeTaskEntity? {\n        return savedTasks.first().find { task -> task.type == type }\n    }\n\n    override suspend fun insertAll(vararg tasks: DailyChallengeTaskEntity) {\n        insertAll(tasks.toList())\n    }\n\n    override suspend fun insertAll(tasks: List<DailyChallengeTaskEntity>) {\n        savedTasks.emit(\n            savedTasks.first() + tasks\n        )\n    }\n\n    override suspend fun update(vararg tasks: DailyChallengeTaskEntity) {\n        updateAll(tasks.toList())\n    }\n\n    override suspend fun updateAll(tasks: List<DailyChallengeTaskEntity>) {\n        savedTasks.update { currentTasks ->\n            currentTasks.toMutableList().apply {\n                tasks.forEach { task ->\n                    val index = indexOfFirst { it.id == task.id }\n                    if (index != -1) {\n                        set(index, task)\n                    }\n                }\n            }\n        }\n    }\n\n    override suspend fun deleteAll() {\n        savedTasks.update { emptyList() }\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/domain/FakeGameResultDao.kt",
    "content": "package com.infinitepower.newquiz.core.testing.domain\n\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.database.model.user.ComparisonQuizGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.MultiChoiceGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.WordleGameResultEntity\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.flow.map\nimport kotlinx.coroutines.flow.update\nimport kotlin.random.Random\n\nclass FakeGameResultDao : GameResultDao {\n    private val multiChoiceResults = MutableStateFlow<List<MultiChoiceGameResultEntity>>(emptyList())\n\n    override suspend fun insertMultiChoiceResult(vararg result: MultiChoiceGameResultEntity) {\n        multiChoiceResults.update { currentList ->\n            currentList.toMutableList().apply {\n                addAll(result.map { it.copy(gameId = Random.nextInt()) })\n            }\n        }\n    }\n\n    override suspend fun getMultiChoiceResults(): List<MultiChoiceGameResultEntity> = multiChoiceResults.first()\n\n    private val wordleResults = MutableStateFlow<List<WordleGameResultEntity>>(emptyList())\n\n    override suspend fun insertWordleResult(vararg result: WordleGameResultEntity) {\n        wordleResults.update { currentList ->\n            currentList.toMutableList().apply {\n                addAll(result.map { it.copy(gameId = Random.nextInt()) })\n            }\n        }\n    }\n\n    override suspend fun getWordleResults(): List<WordleGameResultEntity> = wordleResults.first()\n\n    private val comparisonQuizResults = MutableStateFlow<List<ComparisonQuizGameResultEntity>>(emptyList())\n\n    override suspend fun insertComparisonQuizResult(vararg result: ComparisonQuizGameResultEntity) {\n        comparisonQuizResults.update { currentList ->\n            currentList.toMutableList().apply {\n                addAll(result.map { it.copy(gameId = Random.nextInt()) })\n            }\n        }\n    }\n\n    override suspend fun getComparisonQuizResults(): List<ComparisonQuizGameResultEntity> {\n        return comparisonQuizResults.first()\n    }\n\n    override suspend fun getComparisonQuizHighestPosition(categoryId: String): Int {\n        return comparisonQuizResults.first()\n            .filter { it.categoryId == categoryId }\n            .maxOfOrNull { it.endPosition }\n            ?: 0\n    }\n\n    override fun getComparisonQuizHighestPositionFlow(categoryId: String): Flow<Int> {\n        return comparisonQuizResults.map { results ->\n            results.filter { it.categoryId == categoryId }\n                .maxOfOrNull { it.endPosition }\n                ?: 0\n        }\n    }\n\n    override suspend fun getXpForDateRange(\n        startDate: Long,\n        endDate: Long\n    ): List<GameResultDao.XpForPlayedAt> {\n        return (multiChoiceResults.first() + wordleResults.first() + comparisonQuizResults.first())\n            .filter { it.playedAt in startDate..endDate }\n            .map {\n                GameResultDao.XpForPlayedAt(\n                    earnedXp = it.earnedXp,\n                    playedAt = it.playedAt\n                )\n            }\n    }\n\n    override fun getXpForDateRangeFlow(\n        startDate: Long,\n        endDate: Long\n    ): Flow<List<GameResultDao.XpForPlayedAt>> = combine(\n        multiChoiceResults,\n        wordleResults,\n        comparisonQuizResults\n    ) { multiChoiceResults, wordleResults, comparisonQuizResults ->\n        (multiChoiceResults + wordleResults + comparisonQuizResults)\n            .filter { it.playedAt in startDate..endDate }\n            .map {\n                GameResultDao.XpForPlayedAt(\n                    earnedXp = it.earnedXp,\n                    playedAt = it.playedAt\n                )\n            }\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/ui/theme/TestTheme.kt",
    "content": "package com.infinitepower.newquiz.core.testing.ui.theme\n\nimport androidx.compose.runtime.Composable\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\n\n@Composable\nfun NewQuizTestTheme(\n    darkTheme: Boolean = false,\n    dynamicColor: Boolean = false,\n    content: @Composable () -> Unit\n) {\n    NewQuizTheme(\n        darkTheme = darkTheme,\n        dynamicColor = dynamicColor,\n        content = content\n    )\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/utils/ComposeRule.kt",
    "content": "package com.infinitepower.newquiz.core.testing.utils\n\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.test.junit4.ComposeContentTestRule\nimport com.infinitepower.newquiz.core.testing.ui.theme.NewQuizTestTheme\nimport org.jetbrains.annotations.TestOnly\nimport java.util.Locale\n\n/**\n * Sets the content of the [ComposeContentTestRule] to the given [composable] wrapped in a [NewQuizTestTheme].\n * The [locale] can be used to set the device locale.\n * The [darkTheme] and [dynamicColor] can be used to set the theme.\n * This function is only available in tests.\n *\n * @param locale The locale to set the device to.\n * @param darkTheme Whether to use the dark theme.\n * @param dynamicColor Whether to use dynamic colors.\n * @param composable The composable to set as content.\n */\n@TestOnly\n@VisibleForTesting\nfun ComposeContentTestRule.setTestContent(\n    locale: Locale = Locale.ENGLISH,\n    darkTheme: Boolean = false,\n    dynamicColor: Boolean = false,\n    composable: @Composable () -> Unit\n) {\n    setContent {\n        NewQuizTestTheme(\n            darkTheme = darkTheme,\n            dynamicColor = dynamicColor\n        ) {\n            setTestDeviceLocale(locale = locale)\n\n            Surface {\n                composable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/utils/LocaleUtils.kt",
    "content": "package com.infinitepower.newquiz.core.testing.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.res.Configuration\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.platform.LocalContext\nimport org.jetbrains.annotations.TestOnly\nimport java.util.Locale\n\n@TestOnly\n@VisibleForTesting\n@Suppress(\"DEPRECATION\")\n@SuppressLint(\"AppBundleLocaleChanges\")\nfun setTestDeviceLocale(\n    context: Context,\n    locale: Locale = Locale.ENGLISH\n) {\n    context.resources.apply {\n        val config = Configuration(configuration)\n\n        context.createConfigurationContext(configuration)\n        Locale.setDefault(locale)\n        config.setLocale(locale)\n        context.resources.updateConfiguration(config, displayMetrics)\n    }\n}\n\n@TestOnly\n@Composable\n@VisibleForTesting\n@Suppress(\"ComposableNaming\")\nfun setTestDeviceLocale(\n    locale: Locale = Locale.ENGLISH\n) {\n    setTestDeviceLocale(LocalContext.current, locale)\n}\n"
  },
  {
    "path": "core/testing/src/main/kotlin/com/infinitepower/newquiz/core/testing/utils/LogUtils.kt",
    "content": "package com.infinitepower.newquiz.core.testing.utils\n\nimport android.util.Log\nimport io.mockk.every\nimport io.mockk.mockkStatic\nimport kotlinx.datetime.Clock\n\nfun mockAndroidLog() {\n    mockkStatic(Log::class)\n\n    every { Log.d(any(), any()) } answers {\n        println(\"${Clock.System.now()} DEBUG: ${args[0]} - ${args[1]}\")\n        0\n    }\n\n    every { Log.e(any(), any()) } answers {\n        println(\"${Clock.System.now()} ERROR: ${args[0]} - ${args[1]}\")\n        0\n    }\n\n    every { Log.i(any(), any()) } answers {\n        println(\"${Clock.System.now()} INFO: ${args[0]} - ${args[1]}\")\n        0\n    }\n\n    every { Log.v(any(), any()) } answers {\n        println(\"${Clock.System.now()} VERBOSE: ${args[0]} - ${args[1]}\")\n        0\n    }\n\n    every { Log.w(any(), any<String>()) } answers {\n        println(\"${Clock.System.now()} WARN: ${args[0]} - ${args[1]}\")\n        0\n    }\n}\n"
  },
  {
    "path": "core/testing/src/normal/kotlin/com/infinitepower/newquiz/core/testing/di/RemoteConfigModule.kt",
    "content": "package com.infinitepower.newquiz.core.testing.di\n\nimport android.content.Context\nimport com.infinitepower.newquiz.core.remote_config.LocalDefaultsRemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.initializer.RemoteConfigInitializer\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport dagger.hilt.testing.TestInstallIn\nimport javax.inject.Singleton\n\n@Module\n@TestInstallIn(\n    components = [SingletonComponent::class],\n    replaces = [RemoteConfigInitializer::class]\n)\nobject RemoteConfigModule {\n    @Provides\n    @Singleton\n    fun provideRemoteConfig(\n        @ApplicationContext context: Context\n    ): RemoteConfig = LocalDefaultsRemoteConfig(context).apply {\n        initialize()\n    }\n}"
  },
  {
    "path": "core/testing/src/normal/kotlin/com/infinitepower/newquiz/core/testing/di/TestAnalyticsModule.kt",
    "content": "package com.infinitepower.newquiz.core.testing.di\n\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.analytics.LocalDebugAnalyticsHelper\nimport com.infinitepower.newquiz.core.analytics.NormalAnalyticsModule\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.components.SingletonComponent\nimport dagger.hilt.testing.TestInstallIn\n\n@Module\n@TestInstallIn(\n    components = [SingletonComponent::class],\n    replaces = [NormalAnalyticsModule::class]\n)\nabstract class TestAnalyticsModule {\n    @Binds\n    abstract fun bindAnalyticsHelper(impl: LocalDebugAnalyticsHelper): AnalyticsHelper\n}"
  },
  {
    "path": "core/translation/.gitignore",
    "content": "/build"
  },
  {
    "path": "core/translation/README.md",
    "content": "# Translation Module (:core:translation)\n\nThis module provides a translation service for the game.\n\n## Normal Flavor\n\nThe normal flavor of the translation uses the [Ml Kit](https://developers.google.com/ml-kit/language/translation) from Google.\n\n## FOSS Flavor\n\nCurrently, the translation service is not available in the FOSS flavor.\n"
  },
  {
    "path": "core/translation/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.core.translation\"\n}\n\ndependencies {\n    implementation(projects.model)\n    implementation(projects.core)\n    implementation(projects.core.datastore)\n\n    normalImplementation(libs.google.mlKit.translate)\n    normalImplementation(libs.kotlinx.coroutines.playServices)\n}\n"
  },
  {
    "path": "core/translation/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "core/translation/proguard-rules.pro",
    "content": "# Keep `Companion` object fields of serializable classes.\n# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.\n-if @kotlinx.serialization.Serializable class **\n-keepclassmembers class <1> {\n    static <1>$Companion Companion;\n}\n\n# Keep `serializer()` on companion objects (both default and named) of serializable classes.\n-if @kotlinx.serialization.Serializable class ** {\n    static **$* *;\n}\n-keepclassmembers class <2>$<3> {\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n    public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n    public static <1> INSTANCE;\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.\n-keepattributes RuntimeVisibleAnnotations,AnnotationDefault\n\n-dontwarn java.lang.invoke.StringConcatFactory"
  },
  {
    "path": "core/translation/src/androidTestNormal/kotlin/com/infinitepower/newquiz/translation/GoogleTranslatorUtilTest.kt",
    "content": "package com.infinitepower.newquiz.translation\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport com.google.common.truth.Truth\nimport com.google.mlkit.common.sdkinternal.MlKitContext\nimport com.google.mlkit.nl.translate.TranslateLanguage\nimport com.infinitepower.newquiz.core.common.dataStore.SettingsCommon\nimport com.infinitepower.newquiz.core.dataStore.manager.DataStoreManager\nimport io.mockk.coEvery\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport kotlin.test.BeforeTest\n\n/**\n * Instrumentation tests for [GoogleTranslatorUtil].\n */\n@RunWith(AndroidJunit4::class)\ninternal class GoogleTranslatorUtilTest {\n    private val settingsDataStoreManager = mockk<DataStoreManager>()\n\n    private lateinit var translatorUtil: GoogleTranslatorUtil\n\n    @BeforeTest\n    fun setUp() {\n        val context = InstrumentationRegistry.getInstrumentation().context\n        MlKitContext.initializeIfNeeded(context)\n\n        translatorUtil = GoogleTranslatorUtil(\n            settingsDataStoreManager = settingsDataStoreManager\n        )\n    }\n\n    @Test\n    fun getTranslator_shouldReturnTranslator() = runTest {\n        coEvery {\n            settingsDataStoreManager.getPreference(SettingsCommon.Translation.TargetLanguage)\n        } returns TranslateLanguage.FRENCH\n\n        Truth.assertThat(translatorUtil.getTranslator()).isNotNull()\n    }\n}"
  },
  {
    "path": "core/translation/src/foss/kotlin/com/infinitepower/newquiz/core/translation/NoTranslatorUtil.kt",
    "content": "package com.infinitepower.newquiz.core.translation\n\nimport kotlinx.coroutines.flow.Flow\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\nclass NoTranslatorAvailableException : RuntimeException(\"No translator available\")\n\n/**\n * A [TranslatorUtil] implementation that does nothing.\n * This is used when the translator is not available.\n * The functions will throw [NoTranslatorAvailableException] when called.\n */\n@Singleton\nclass NoTranslatorUtil @Inject constructor() : TranslatorUtil {\n    override suspend fun isReadyToTranslate(): Boolean = false\n\n    override suspend fun isModelDownloaded(): Boolean = false\n\n    override val availableTargetLanguageCodes: List<String> = emptyList()\n\n    override val availableTargetLanguages: TranslatorTargetLanguages = emptyMap()\n\n    override suspend fun getTargetLanguageCode(): String {\n        throw NoTranslatorAvailableException()\n    }\n\n    override suspend fun downloadModel(\n        targetLanguage: String,\n        requireWifi: Boolean,\n        requireCharging: Boolean\n    ): Flow<TranslatorModelState> {\n        throw NoTranslatorAvailableException()\n    }\n\n    override suspend fun deleteModel() {\n        throw NoTranslatorAvailableException()\n    }\n\n    override suspend fun translate(text: String): String {\n        throw NoTranslatorAvailableException()\n    }\n\n    override suspend fun translate(items: List<String>): List<String> {\n        throw NoTranslatorAvailableException()\n    }\n}"
  },
  {
    "path": "core/translation/src/foss/kotlin/com/infinitepower/newquiz/core/translation/di/TranslatorModule.kt",
    "content": "package com.infinitepower.newquiz.core.translation.di\n\nimport com.infinitepower.newquiz.core.translation.NoTranslatorUtil\nimport com.infinitepower.newquiz.core.translation.TranslatorUtil\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\n\n@Module\n@InstallIn(SingletonComponent::class)\nabstract class NoTranslatorModule {\n    @Binds\n    abstract fun bindTranslatorUtil(impl: NoTranslatorUtil): TranslatorUtil\n}"
  },
  {
    "path": "core/translation/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />"
  },
  {
    "path": "core/translation/src/main/kotlin/com/infinitepower/newquiz/core/translation/TranslatorLanguageSettings.kt",
    "content": "package com.infinitepower.newquiz.core.translation\n\n/**\n * Language settings for the translator.\n *\n * key - language code\n *\n * value - language name\n */\ntypealias TranslatorTargetLanguages = Map<String, String>\n"
  },
  {
    "path": "core/translation/src/main/kotlin/com/infinitepower/newquiz/core/translation/TranslatorModelState.kt",
    "content": "package com.infinitepower.newquiz.core.translation\n\nenum class TranslatorModelState {\n    None,\n    Downloaded,\n    Downloading\n}\n"
  },
  {
    "path": "core/translation/src/main/kotlin/com/infinitepower/newquiz/core/translation/TranslatorUtil.kt",
    "content": "package com.infinitepower.newquiz.core.translation\n\nimport kotlinx.coroutines.flow.Flow\n\ninterface TranslatorUtil {\n    /**\n     * @return true if the translator is ready to translate, when the model is downloaded\n     * and the target language is set and the translation is enabled, false otherwise\n     */\n    suspend fun isReadyToTranslate(): Boolean\n\n    /**\n     * @return true if the model is downloaded, false otherwise\n     */\n    suspend fun isModelDownloaded(): Boolean\n\n    /**\n     * @return the list of available language codes\n     */\n    val availableTargetLanguageCodes: List<String>\n\n    /**\n     * @return the list of available [TranslatorTargetLanguages] for the target languages.\n     * The key is the language code, the value is the language name.\n     */\n    val availableTargetLanguages: TranslatorTargetLanguages\n\n    /**\n     * @return the current target language code\n     */\n    suspend fun getTargetLanguageCode(): String\n\n    /**\n     * Downloads the translation model for the current target language.\n     */\n    suspend fun downloadModel(\n        targetLanguage: String,\n        requireWifi: Boolean,\n        requireCharging: Boolean\n    ): Flow<TranslatorModelState>\n\n    /**\n     * Deletes the current translation model\n     */\n    suspend fun deleteModel()\n\n    /**\n     * Translates the given [text] to the current target language.\n     */\n    suspend fun translate(text: String): String\n\n    /**\n     * Translates the given [items] to the current target language.\n     */\n    suspend fun translate(items: List<String>): List<String>\n}\n"
  },
  {
    "path": "core/translation/src/normal/kotlin/com/infinitepower/newquiz/core/translation/GoogleTranslatorUtil.kt",
    "content": "package com.infinitepower.newquiz.core.translation\n\nimport android.os.Build\nimport com.google.mlkit.common.model.DownloadConditions\nimport com.google.mlkit.common.model.RemoteModelManager\nimport com.google.mlkit.nl.translate.TranslateLanguage\nimport com.google.mlkit.nl.translate.TranslateRemoteModel\nimport com.google.mlkit.nl.translate.Translation\nimport com.google.mlkit.nl.translate.Translator\nimport com.google.mlkit.nl.translate.TranslatorOptions\nimport com.infinitepower.newquiz.core.datastore.common.TranslationCommon\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.flow\nimport kotlinx.coroutines.tasks.await\nimport java.util.Locale\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass GoogleTranslatorUtil @Inject constructor(\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager\n) : TranslatorUtil {\n    override suspend fun isReadyToTranslate(): Boolean {\n        val translationEnabled = settingsDataStoreManager.getPreference(TranslationCommon.Enabled)\n\n        return translationEnabled && isModelDownloaded()\n    }\n\n    override suspend fun isModelDownloaded(): Boolean {\n        val localeLanguage = getTargetLanguageCode()\n\n        // If the locale language is empty, then the model is not downloaded\n        if (localeLanguage.isEmpty()) {\n            return false\n        }\n\n        val localeModel = TranslateRemoteModel\n            .Builder(localeLanguage)\n            .build()\n\n        return RemoteModelManager\n            .getInstance()\n            .isModelDownloaded(localeModel)\n            .await()\n    }\n\n    override val availableTargetLanguageCodes: List<String> by lazy {\n        TranslateLanguage.getAllLanguages().filter { languageCode ->\n            // Remove English from the list of available languages\n            // because we don't want to translate to English\n            languageCode != TranslateLanguage.ENGLISH\n        }\n    }\n\n    override val availableTargetLanguages: TranslatorTargetLanguages by lazy {\n        // Associate the language code with the language name\n        availableTargetLanguageCodes.associateWith { languageCode ->\n            // Get the locale for the given language code\n            val locale = Locale(languageCode)\n\n            locale.getDisplayName(locale)\n        }\n    }\n\n    override suspend fun getTargetLanguageCode(): String {\n        return settingsDataStoreManager.getPreference(TranslationCommon.TargetLanguage)\n    }\n\n    suspend fun getTranslator(): Translator {\n        val targetLanguage = getTargetLanguageCode()\n\n        if (targetLanguage.isEmpty()) {\n            throw IllegalStateException(\"Target language is empty\")\n        }\n\n        if (targetLanguage == TranslateLanguage.ENGLISH) {\n            throw IllegalStateException(\"Target language cannot be English\")\n        }\n\n        return getTranslator(targetLanguage)\n    }\n\n    private fun getTranslator(targetLanguage: String): Translator {\n        val options = TranslatorOptions\n            .Builder()\n            .setSourceLanguage(TranslateLanguage.ENGLISH)\n            .setTargetLanguage(targetLanguage)\n            .build()\n\n        return Translation.getClient(options)\n    }\n\n    override suspend fun downloadModel(\n        targetLanguage: String,\n        requireWifi: Boolean,\n        requireCharging: Boolean\n    ): Flow<TranslatorModelState> = flow {\n        emit(TranslatorModelState.Downloading)\n\n        // Get the translator\n        val translator = getTranslator(targetLanguage)\n\n        val conditions = DownloadConditions\n            .Builder()\n            .apply {\n                if (requireWifi) requireWifi()\n\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && requireCharging){\n                    requireCharging()\n                }\n            }.build()\n\n        // Download the model\n        translator\n            .downloadModelIfNeeded(conditions)\n            .await()\n\n        emit(TranslatorModelState.Downloaded)\n    }\n\n    override suspend fun deleteModel() {\n        // Get the current target language\n        val localeLanguage = getTargetLanguageCode()\n\n        val localeModel = TranslateRemoteModel\n            .Builder(localeLanguage)\n            .build()\n\n        // Delete the model\n        RemoteModelManager\n            .getInstance()\n            .deleteDownloadedModel(localeModel)\n            .await()\n    }\n\n    override suspend fun translate(text: String): String {\n        val translator = getTranslator()\n\n        return translate(text, translator)\n    }\n\n    override suspend fun translate(items: List<String>): List<String> {\n        val translator = getTranslator()\n\n        return items.map { item ->\n            translate(item, translator)\n        }\n    }\n\n    /**\n     * Translate the given [text] using the given [translator].\n     *\n     * @return the translated text\n     */\n    private suspend fun translate(text: String, translator: Translator): String {\n        return translator.translate(text).await()\n    }\n}"
  },
  {
    "path": "core/translation/src/normal/kotlin/com/infinitepower/newquiz/core/translation/di/GoogleTranslatorModule.kt",
    "content": "package com.infinitepower.newquiz.core.translation.di\n\nimport com.infinitepower.newquiz.core.translation.GoogleTranslatorUtil\nimport com.infinitepower.newquiz.core.translation.TranslatorUtil\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\n\n@Module\n@InstallIn(SingletonComponent::class)\nabstract class GoogleTranslatorModule {\n    @Binds\n    abstract fun bindTranslatorUtil(impl: GoogleTranslatorUtil): TranslatorUtil\n}"
  },
  {
    "path": "core/translation/src/testFoss/kotlin/com/infinitepower/newquiz/core/translation/NoTranslatorUtilTest.kt",
    "content": "package com.infinitepower.newquiz.core.translation\n\nimport com.google.common.truth.Truth.assertThat\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.api.assertThrows\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [NoTranslatorUtil].\n */\ninternal class NoTranslatorUtilTest {\n    private lateinit var translatorUtil: TranslatorUtil\n\n    @BeforeTest\n    fun setUp() {\n        translatorUtil = NoTranslatorUtil()\n    }\n\n    @Test\n    fun `translator should not be available and downloaded`() {\n        runTest {\n            assertThat(translatorUtil.isModelDownloaded()).isFalse()\n            assertThat(translatorUtil.isReadyToTranslate()).isFalse()\n        }\n    }\n\n    @Test\n    fun `should return empty list of available target language codes and languages`() {\n        assertThat(translatorUtil.availableTargetLanguageCodes).isEmpty()\n        assertThat(translatorUtil.availableTargetLanguages).isEmpty()\n    }\n\n    @Test\n    fun `should throw exception when getting target language code`() {\n        assertThrows<NoTranslatorAvailableException> {\n            runTest {\n                translatorUtil.getTargetLanguageCode()\n            }\n        }\n    }\n\n    @Test\n    fun `should throw exception when downloading model`() {\n        assertThrows<NoTranslatorAvailableException> {\n            runTest {\n                translatorUtil.downloadModel(\n                    targetLanguage = \"en\",\n                    requireWifi = false,\n                    requireCharging = false\n                )\n            }\n        }\n    }\n\n    @Test\n    fun `should throw exception when deleting model`() {\n        assertThrows<NoTranslatorAvailableException> {\n            runTest {\n                translatorUtil.deleteModel()\n            }\n        }\n    }\n\n    @Test\n    fun `should throw exception when translating text`() {\n        assertThrows<NoTranslatorAvailableException> {\n            runTest {\n                translatorUtil.translate(\"Hello\")\n            }\n        }\n    }\n\n    @Test\n    fun `should throw exception when translating list of items`() {\n        assertThrows<NoTranslatorAvailableException> {\n            runTest {\n                translatorUtil.translate(listOf(\"Hello\", \"World\"))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/translation/src/testNormal/kotlin/com/infinitepower/newquiz/core/translation/GoogleTranslatorUtilTest.kt",
    "content": "package com.infinitepower.newquiz.core.translation\n\nimport com.google.common.truth.Truth.assertThat\nimport com.google.mlkit.nl.translate.TranslateLanguage\nimport com.infinitepower.newquiz.core.datastore.common.TranslationCommon\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport io.mockk.coEvery\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ValueSource\nimport java.util.Locale\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [GoogleTranslatorUtil].\n */\ninternal class GoogleTranslatorUtilTest {\n    private val settingsDataStoreManager = mockk<DataStoreManager>()\n\n    private lateinit var translatorUtil: GoogleTranslatorUtil\n\n    @BeforeTest\n    fun setUp() {\n        translatorUtil = GoogleTranslatorUtil(\n            settingsDataStoreManager = settingsDataStoreManager\n        )\n    }\n\n    @ParameterizedTest\n    @ValueSource(strings = [\"\", \"en\", \"fr\", \"de\"])\n    fun `getTargetLanguageCode should return the target language`(\n        targetLanguage: String\n    ) = runTest {\n        coEvery {\n            settingsDataStoreManager.getPreference(TranslationCommon.TargetLanguage)\n        } returns targetLanguage\n\n        assertThat(translatorUtil.getTargetLanguageCode()).isEqualTo(targetLanguage)\n    }\n\n    @Test\n    fun `availableTargetLanguageCodes should return all available languages`() {\n        val expectLanguages = TranslateLanguage.getAllLanguages().filter { languageCode ->\n            // Remove English from the list of available languages\n            // because we don't want to translate to English\n            languageCode != TranslateLanguage.ENGLISH\n        }\n\n        assertThat(translatorUtil.availableTargetLanguageCodes).containsExactlyElementsIn(\n            expectLanguages\n        )\n    }\n\n    @Test\n    fun `availableTargetLanguages should return all available languages`() {\n        val expectLanguages = TranslateLanguage.getAllLanguages().filter { languageCode ->\n            // Remove English from the list of available languages\n            // because we don't want to translate to English\n            languageCode != TranslateLanguage.ENGLISH\n        }.associateWith { languageCode ->\n            // Get the locale for the given language code\n            val locale = Locale(languageCode)\n\n            locale.getDisplayName(locale)\n        }\n\n        assertThat(translatorUtil.availableTargetLanguages).containsExactlyEntriesIn(expectLanguages)\n    }\n\n    @Test\n    fun `getTranslator should throw IllegalStateException when target language is empty`() =\n        runTest {\n            coEvery {\n                settingsDataStoreManager.getPreference(TranslationCommon.TargetLanguage)\n            } returns \"\"\n\n            assertThrows<IllegalStateException> {\n                translatorUtil.getTranslator()\n            }\n        }\n\n    @Test\n    fun `getTranslator should throw IllegalStateException when target language is English`() =\n        runTest {\n            coEvery {\n                settingsDataStoreManager.getPreference(TranslationCommon.TargetLanguage)\n            } returns TranslateLanguage.ENGLISH\n\n            assertThrows<IllegalStateException> {\n                translatorUtil.getTranslator()\n            }\n        }\n}"
  },
  {
    "path": "core/user-services/.gitignore",
    "content": "/build"
  },
  {
    "path": "core/user-services/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.core.user_services\"\n}\n\ndependencies {\n    implementation(projects.model)\n    implementation(projects.core)\n    implementation(projects.core.analytics)\n    implementation(projects.core.database)\n    implementation(projects.core.datastore)\n    implementation(projects.core.remoteConfig)\n    androidTestImplementation(projects.core.testing)\n\n    implementation(libs.hilt.ext.work)\n    implementation(libs.androidx.work.ktx)\n    ksp(libs.hilt.ext.compiler)\n    androidTestImplementation(libs.androidx.work.testing)\n\n    implementation(libs.kotlinx.datetime)\n\n    testImplementation(projects.core.testing)\n}\n"
  },
  {
    "path": "core/user-services/src/androidTest/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n    <application>\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            tools:node=\"remove\" />\n    </application>\n</manifest>"
  },
  {
    "path": "core/user-services/src/androidTest/kotlin/com/infinitepower/newquiz/core/user_services/LocalUserServiceImplTest.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.AppDatabase\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.datastore.common.LocalUserCommon\nimport com.infinitepower.newquiz.core.datastore.di.LocalUserDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.user_services.domain.xp.ComparisonQuizXpGenerator\nimport com.infinitepower.newquiz.core.user_services.domain.xp.MultiChoiceQuizXpGenerator\nimport com.infinitepower.newquiz.core.user_services.domain.xp.WordleXpGenerator\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport io.mockk.coEvery\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Rule\nimport org.junit.runner.RunWith\nimport javax.inject.Inject\nimport kotlin.test.AfterTest\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [LocalUserServiceImpl]\n */\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\ninternal class LocalUserServiceImplTest {\n    @get:Rule\n    val hiltRule = HiltAndroidRule(this)\n\n    private lateinit var localUserServiceImpl: LocalUserServiceImpl\n\n    private val remoteConfig: RemoteConfig = mockk()\n\n    @Inject\n    @LocalUserDataStoreManager\n    lateinit var dataStoreManager: DataStoreManager\n\n    @Inject\n    lateinit var appDatabase: AppDatabase\n\n    @Inject\n    lateinit var gameResultDao: GameResultDao\n\n    @Inject\n    lateinit var multiChoiceQuizXpGenerator: MultiChoiceQuizXpGenerator\n\n    @Inject\n    lateinit var wordleXpGenerator: WordleXpGenerator\n\n    @Inject\n    lateinit var comparisonQuizXpGenerator: ComparisonQuizXpGenerator\n\n    companion object {\n        private const val INITIAL_DIAMONDS = 10\n        private const val NEW_LEVEL_DIAMONDS = 10\n    }\n\n    @BeforeTest\n    fun setUp() {\n        hiltRule.inject()\n\n        runTest {\n            dataStoreManager.clearPreferences()\n\n            dataStoreManager.editPreference(LocalUserCommon.UserUid.key, \"uid\")\n            dataStoreManager.editPreference(LocalUserCommon.UserTotalXp.key, 0)\n            dataStoreManager.editPreference(\n                LocalUserCommon.UserDiamonds(INITIAL_DIAMONDS).key,\n                INITIAL_DIAMONDS\n            )\n        }\n\n        localUserServiceImpl = LocalUserServiceImpl(\n            dataStoreManager = dataStoreManager,\n            remoteConfig = remoteConfig,\n            gameResultDao = gameResultDao,\n            multiChoiceXpGenerator = multiChoiceQuizXpGenerator,\n            wordleXpGenerator = wordleXpGenerator,\n            comparisonQuizXpGenerator = comparisonQuizXpGenerator\n        )\n    }\n\n    @AfterTest\n    fun tearDown() {\n        appDatabase.close()\n\n        runTest {\n            dataStoreManager.clearPreferences()\n        }\n    }\n\n    @Test\n    fun test_saveMultiChoiceGame_whenGenerateXpIsEnabled() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        // Average answer time is 8 seconds\n        val questionSteps = listOf(\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true,\n                questionTime = 10\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = false,\n                questionTime = 8\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true,\n                questionTime = 6\n            ),\n        )\n\n        val initialUser = localUserServiceImpl.getUser()\n        require(initialUser != null)\n\n        localUserServiceImpl.saveMultiChoiceGame(\n            questionSteps = questionSteps,\n            generateXp = true\n        )\n\n        // Check that the user's total xp has been updated\n        val updatedUser = localUserServiceImpl.getUser()\n        require(updatedUser != null)\n\n        val expectedXp = multiChoiceQuizXpGenerator.generateXp(questionSteps)\n\n        // Check if the new xp is in the range of the generated xp\n        val newXp = updatedUser.totalXp - initialUser.totalXp\n        assertThat(newXp.toInt()).isEqualTo(expectedXp.toInt())\n\n        // If the user is in new level, check if the diamonds have been updated\n        if (initialUser.isNewLevel(newXp)) {\n            assertThat(updatedUser.diamonds).isEqualTo(updatedUser.diamonds + NEW_LEVEL_DIAMONDS.toUInt())\n        } else {\n            assertThat(updatedUser.diamonds).isEqualTo(initialUser.diamonds)\n        }\n\n        // Check if the game result has been saved\n        val gameResults = gameResultDao.getMultiChoiceResults()\n\n        assertThat(gameResults).hasSize(1)\n\n        // Because there is only one game result, we can assume that the first one is the one we want\n        gameResults.first().apply {\n            assertThat(correctAnswers).isEqualTo(questionSteps.count { it.correct })\n            assertThat(questionCount).isEqualTo(questionSteps.count())\n            assertThat(averageAnswerTime).isEqualTo(\n                questionSteps.map { it.questionTime }.average()\n            )\n            assertThat(earnedXp).isEqualTo(newXp.toInt())\n        }\n    }\n\n    @Test\n    fun test_saveMultiChoiceGame_whenGenerateXpIsDisabled() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        // Average answer time is 8 seconds\n        val questionSteps = listOf(\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true,\n                questionTime = 10\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = false,\n                questionTime = 8\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true,\n                questionTime = 6\n            ),\n        )\n\n        val initialUser = localUserServiceImpl.getUser()\n        require(initialUser != null)\n\n        localUserServiceImpl.saveMultiChoiceGame(\n            questionSteps = questionSteps,\n            generateXp = false\n        )\n\n        val updatedUser = localUserServiceImpl.getUser()\n        require(updatedUser != null)\n\n        // because the xp generation is disabled, the new xp should be 0\n        val newXp = updatedUser.totalXp - initialUser.totalXp\n        assertThat(newXp).isEqualTo(0uL)\n\n        // Check if the game result has been saved\n        val gameResults = gameResultDao.getMultiChoiceResults()\n\n        assertThat(gameResults).hasSize(1)\n    }\n}"
  },
  {
    "path": "core/user-services/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" />"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/DateTimeRangeFormatter.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nimport com.infinitepower.newquiz.model.TimestampWithXP\nimport com.infinitepower.newquiz.model.XP\nimport kotlinx.datetime.Clock\nimport kotlinx.datetime.Instant\nimport kotlinx.datetime.LocalDate\nimport kotlinx.datetime.LocalTime\nimport kotlinx.datetime.TimeZone\nimport kotlinx.datetime.atStartOfDayIn\nimport kotlinx.datetime.toJavaLocalDate\nimport kotlinx.datetime.toJavaLocalTime\nimport kotlinx.datetime.toLocalDateTime\nimport java.time.format.DateTimeFormatter\nimport java.time.format.FormatStyle\nimport kotlin.time.Duration.Companion.days\n\ntypealias GroupingKey = Int\ntypealias XPByGroupingKey = Map<GroupingKey, XP>\ntypealias TimeRange = Pair<Instant, Instant>\n\nsealed interface DateTimeRangeFormatter {\n    fun groupingKey(\n        timestamp: Long,\n        tz: TimeZone = TimeZone.currentSystemDefault()\n    ): GroupingKey\n\n    val formatter: DateTimeFormatter\n\n    fun formatValueToString(value: GroupingKey): String\n\n    fun aggregateResults(\n        results: List<TimestampWithXP>,\n        tz: TimeZone = TimeZone.currentSystemDefault()\n    ): XPByGroupingKey {\n        return results\n            .groupBy { groupingKey(it.timestamp, tz) }\n            .mapValues { (_, values) ->\n                values.sumOf { it.value }\n            }.toSortedMap()\n    }\n\n    fun getNowDateTimeRange(\n        now: Instant = Clock.System.now(),\n        tz: TimeZone = TimeZone.currentSystemDefault()\n    ): TimeRange\n\n    data object Day : DateTimeRangeFormatter {\n        override fun groupingKey(\n            timestamp: Long,\n            tz: TimeZone\n        ): GroupingKey {\n            return Instant.fromEpochMilliseconds(timestamp).toLocalDateTime(tz).hour\n        }\n\n        override val formatter: DateTimeFormatter by lazy {\n            DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)\n        }\n\n        override fun formatValueToString(value: GroupingKey): String {\n            return LocalTime(value, 0).toJavaLocalTime().format(formatter)\n        }\n\n        override fun getNowDateTimeRange(\n            now: Instant,\n            tz: TimeZone\n        ): TimeRange {\n            val start = now.toLocalDateTime(tz).date.atStartOfDayIn(tz)\n\n            return start to now\n        }\n    }\n\n    data object Week : DateTimeRangeFormatter {\n        override fun groupingKey(\n            timestamp: Long,\n            tz: TimeZone\n        ): GroupingKey {\n            return Instant.fromEpochMilliseconds(timestamp).toLocalDateTime(tz).date.toEpochDays()\n        }\n\n        override val formatter: DateTimeFormatter by lazy {\n            DateTimeFormatter.ofPattern(\"d MMM\")\n        }\n\n        override fun formatValueToString(value: GroupingKey): String {\n            return LocalDate.fromEpochDays(value).toJavaLocalDate().format(formatter)\n        }\n\n        override fun getNowDateTimeRange(\n            now: Instant,\n            tz: TimeZone\n        ): TimeRange {\n            val start = now - 7.days\n\n            return start to now\n        }\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/GameResultTracker.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\n\ninterface GameResultTracker {\n    suspend fun saveMultiChoiceGame(\n        questionSteps: List<MultiChoiceQuestionStep.Completed>,\n        generateXp: Boolean\n    )\n\n    suspend fun saveWordleGame(\n        wordLength: UInt,\n        rowsUsed: UInt,\n        maxRows: Int,\n        categoryId: String,\n        generateXp: Boolean\n    )\n\n    suspend fun saveComparisonQuizGame(\n        categoryId: String,\n        comparisonMode: String,\n        endPosition: UInt,\n        skippedAnswers: UInt,\n        generateXp: Boolean\n    )\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/InsufficientDiamondsException.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nclass InsufficientDiamondsException(diamondsNeeded: UInt, diamondsAvailable: UInt) : Exception(\n    \"Not enough diamonds. Need $diamondsNeeded, but only $diamondsAvailable are available.\"\n)\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/LocalUserService.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\ninterface LocalUserService : UserService\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/LocalUserServiceImpl.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.database.model.user.ComparisonQuizGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.MultiChoiceGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.WordleGameResultEntity\nimport com.infinitepower.newquiz.core.datastore.common.LocalUserCommon\nimport com.infinitepower.newquiz.core.datastore.di.LocalUserDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.user_services.domain.xp.ComparisonQuizXpGenerator\nimport com.infinitepower.newquiz.core.user_services.domain.xp.MultiChoiceQuizXpGenerator\nimport com.infinitepower.newquiz.core.user_services.domain.xp.WordleXpGenerator\nimport com.infinitepower.newquiz.core.user_services.model.User\nimport com.infinitepower.newquiz.model.TimestampWithXP\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.map\nimport kotlinx.datetime.Instant\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass LocalUserServiceImpl @Inject constructor(\n    @LocalUserDataStoreManager private val dataStoreManager: DataStoreManager,\n    private val remoteConfig: RemoteConfig,\n    private val gameResultDao: GameResultDao,\n    private val multiChoiceXpGenerator: MultiChoiceQuizXpGenerator,\n    private val wordleXpGenerator: WordleXpGenerator,\n    private val comparisonQuizXpGenerator: ComparisonQuizXpGenerator\n) : LocalUserService {\n    override suspend fun userAvailable(): Boolean {\n        val uid = dataStoreManager.getPreference(LocalUserCommon.UserUid)\n        return uid.isNotBlank()\n    }\n\n    override suspend fun getUser(): User? {\n        val uid = dataStoreManager.getPreference(LocalUserCommon.UserUid)\n        if (uid.isBlank()) return null\n\n        val totalXp = dataStoreManager.getPreference(LocalUserCommon.UserTotalXp)\n\n        val diamonds = getUserDiamonds()\n\n        return User(\n            uid = uid,\n            totalXp = totalXp.toULong(),\n            diamonds = diamonds\n        )\n    }\n\n    override suspend fun getXpEarnedBy(start: Instant, end: Instant): List<TimestampWithXP> {\n        val xpForDateRange = gameResultDao.getXpForDateRange(\n            startDate = start.toEpochMilliseconds(),\n            endDate = end.toEpochMilliseconds()\n        )\n\n        return xpForDateRange.map {\n            TimestampWithXP(\n                timestamp = it.playedAt,\n                value = it.earnedXp\n            )\n        }\n    }\n\n    override suspend fun getXpEarnedBy(timeRange: TimeRange): List<TimestampWithXP> {\n        return getXpEarnedBy(\n            start = timeRange.first,\n            end = timeRange.second,\n        )\n    }\n\n    override fun getXpEarnedByFlow(timeRange: TimeRange): Flow<List<TimestampWithXP>> {\n        return gameResultDao.getXpForDateRangeFlow(\n            startDate = timeRange.first.toEpochMilliseconds(),\n            endDate = timeRange.second.toEpochMilliseconds()\n        ).map { xpForDateRange ->\n            xpForDateRange.map {\n                TimestampWithXP(\n                    timestamp = it.playedAt,\n                    value = it.earnedXp\n                )\n            }\n        }\n    }\n\n    override suspend fun getUserDiamonds(): UInt {\n        val initialDiamonds = remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS)\n        val diamonds =\n            dataStoreManager.getPreference(LocalUserCommon.UserDiamonds(initialDiamonds))\n\n        return diamonds.toUInt()\n    }\n\n    override fun getUserDiamondsFlow(): Flow<UInt> {\n        val initialDiamonds = remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS)\n\n        return dataStoreManager\n            .getPreferenceFlow(LocalUserCommon.UserDiamonds(initialDiamonds))\n            .map(Int::toUInt)\n    }\n\n    override suspend fun addRemoveDiamonds(diamonds: Int) {\n        val initialDiamonds = remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS)\n        val currentDiamonds =\n            dataStoreManager.getPreference(LocalUserCommon.UserDiamonds(initialDiamonds))\n        val newDiamonds = currentDiamonds + diamonds\n\n        dataStoreManager.editPreference(\n            key = LocalUserCommon.UserDiamonds(initialDiamonds).key,\n            newValue = newDiamonds\n        )\n    }\n\n    suspend fun updateNewLevelDiamonds() {\n        val newLevelDiamonds = remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS)\n\n        addRemoveDiamonds(newLevelDiamonds)\n    }\n\n    private fun List<MultiChoiceQuestionStep.Completed>.getAverageQuizTime(): Double {\n        return map(MultiChoiceQuestionStep.Completed::questionTime).average()\n    }\n\n    private suspend fun saveNewXP(newXp: UInt) {\n        val currentUser = getUser()\n        checkNotNull(currentUser) { \"User not found\" }\n\n        val newTotalXp = currentUser.totalXp + newXp\n\n        // Save the new total xp\n        dataStoreManager.editPreference(\n            key = LocalUserCommon.UserTotalXp.key,\n            newValue = newTotalXp.toLong()\n        )\n\n        // Check if the user is in a new level\n        val isNewLevel = currentUser.isNewLevel(newXp = newXp.toULong())\n\n        // If is new level, update the user diamonds\n        if (isNewLevel) {\n            updateNewLevelDiamonds()\n        }\n    }\n\n    override suspend fun saveMultiChoiceGame(\n        questionSteps: List<MultiChoiceQuestionStep.Completed>,\n        generateXp: Boolean\n    ) {\n        var newXp = 0u\n\n        // Generate xp if needed\n        if (generateXp) {\n            // Generate and get the new xp\n            newXp = multiChoiceXpGenerator.generateXp(questionSteps)\n            saveNewXP(newXp)\n        }\n\n        // Save the game result\n        val correctAnswers = questionSteps.count { it.correct }\n        val skippedQuestions = questionSteps.count { it.skipped }\n        val averageAnswerTime = questionSteps.getAverageQuizTime()\n\n        gameResultDao.insertMultiChoiceResult(\n            MultiChoiceGameResultEntity(\n                correctAnswers = correctAnswers,\n                questionCount = questionSteps.size,\n                skippedQuestions = skippedQuestions,\n                averageAnswerTime = averageAnswerTime,\n                earnedXp = newXp.toInt(),\n                playedAt = System.currentTimeMillis()\n            )\n        )\n    }\n\n    override suspend fun saveWordleGame(\n        wordLength: UInt,\n        rowsUsed: UInt,\n        maxRows: Int,\n        categoryId: String,\n        generateXp: Boolean\n    ) {\n        var newXp = 0u\n\n        // Generate xp if needed\n        if (generateXp) {\n            // Generate and get the new xp\n            newXp = wordleXpGenerator.generateXp(rowsUsed)\n            saveNewXP(newXp)\n        }\n\n        // Save the game result\n        gameResultDao.insertWordleResult(\n            WordleGameResultEntity(\n                earnedXp = newXp.toInt(),\n                playedAt = System.currentTimeMillis(),\n                wordLength = wordLength.toInt(),\n                rowsUsed = rowsUsed.toInt(),\n                maxRows = maxRows,\n                categoryId = categoryId\n            )\n        )\n    }\n\n    override suspend fun saveComparisonQuizGame(\n        categoryId: String,\n        comparisonMode: String,\n        endPosition: UInt,\n        skippedAnswers: UInt,\n        generateXp: Boolean\n    ) {\n        var newXp = 0u\n\n        // Generate xp if needed\n        if (generateXp) {\n            // Generate and get the new xp\n            newXp = comparisonQuizXpGenerator.generateXp(\n                endPosition = endPosition,\n                skippedAnswers = skippedAnswers\n            )\n            saveNewXP(newXp)\n        }\n\n        // Save the game result\n        gameResultDao.insertComparisonQuizResult(\n            ComparisonQuizGameResultEntity(\n                earnedXp = newXp.toInt(),\n                playedAt = System.currentTimeMillis(),\n                categoryId = categoryId,\n                comparisonMode = comparisonMode,\n                endPosition = endPosition.toInt(),\n                skippedAnswers = skippedAnswers.toInt()\n            )\n        )\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/UserService.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nimport com.infinitepower.newquiz.core.user_services.model.User\nimport kotlinx.coroutines.flow.Flow\n\ninterface UserService : GameResultTracker, XpManager {\n    /**\n     * @return true if the user is available, false otherwise\n     */\n    suspend fun userAvailable(): Boolean\n\n    suspend fun getUser(): User?\n\n    suspend fun getUserDiamonds(): UInt\n\n    fun getUserDiamondsFlow(): Flow<UInt>\n\n    /**\n     * @param diamonds the amount of diamonds to add/remove\n     */\n    suspend fun addRemoveDiamonds(diamonds: Int)\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/XpManager.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nimport com.infinitepower.newquiz.model.TimestampWithXP\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.datetime.Instant\n\ninterface XpManager {\n    suspend fun getXpEarnedBy(start: Instant, end: Instant): List<TimestampWithXP>\n\n    suspend fun getXpEarnedBy(timeRange: TimeRange): List<TimestampWithXP>\n\n    fun getXpEarnedByFlow(timeRange: TimeRange): Flow<List<TimestampWithXP>>\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/data/xp/ComparisonQuizXpGeneratorImpl.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.data.xp\n\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.user_services.domain.xp.ComparisonQuizXpGenerator\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass ComparisonQuizXpGeneratorImpl @Inject constructor(\n    private val remoteConfig: RemoteConfig\n) : ComparisonQuizXpGenerator {\n    override fun getDefaultXpForAnswer(): UInt {\n        return remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_DEFAULT_XP_REWARD).toUInt()\n    }\n\n    override fun generateXp(endPosition: UInt, skippedAnswers: UInt): UInt {\n        // If the user answered incorrectly, then no XP is awarded\n        if (endPosition == 1.toUInt()) return 0u\n\n        // Calculate the number of answers the user answered without skipping\n        // Example: End position is 4, skipped answers is 1, then the user answered 2 questions\n        // without skipping\n        val answersNotSkipped = endPosition - skippedAnswers - 1u\n\n        return getDefaultXpForAnswer() * answersNotSkipped\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/data/xp/MultiChoiceQuizXpGeneratorImpl.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.data.xp\n\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.user_services.domain.xp.MultiChoiceQuizXpGenerator\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport kotlinx.serialization.json.Json\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass MultiChoiceQuizXpGeneratorImpl @Inject constructor(\n    private val remoteConfig: RemoteConfig\n) : MultiChoiceQuizXpGenerator {\n    override fun getDefaultXpReward(): Map<QuestionDifficulty, UInt> {\n        val xpRewardStr = remoteConfig.get(RemoteConfigValue.MULTICHOICE_QUIZ_DEFAULT_XP_REWARD)\n        val xpReward: Map<String, Int> = Json.decodeFromString(xpRewardStr)\n\n        return xpReward.map { (difficultyStr, reward) ->\n            QuestionDifficulty.from(difficultyStr) to reward.toUInt()\n        }.toMap()\n    }\n\n    override fun generateXp(\n        questionSteps: List<MultiChoiceQuestionStep.Completed>\n    ): UInt {\n        val defaultXpReward = getDefaultXpReward()\n\n        return questionSteps\n            .filter { step ->\n                step.correct && !step.skipped\n            }.sumOf { step ->\n                val difficulty = step.question.difficulty\n\n                defaultXpReward[difficulty] ?: 0u\n            }\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/data/xp/WordleXpGeneratorImpl.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.data.xp\n\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.user_services.domain.xp.WordleXpGenerator\nimport com.infinitepower.newquiz.core.util.kotlin.roundToUInt\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.math.sqrt\n\n@Singleton\nclass WordleXpGeneratorImpl @Inject constructor(\n    private val remoteConfig: RemoteConfig\n) : WordleXpGenerator {\n    override fun getDefaultXp(): UInt {\n        return remoteConfig.get(RemoteConfigValue.WORDLE_DEFAULT_XP_REWARD).toUInt()\n    }\n\n    override fun generateXp(rowsUsed: UInt): UInt {\n        val defaultXp = getDefaultXp()\n\n        // Calculate the XP multiplier based on the number of rows used.\n        // The XP multiplier is inversely proportional to the square root of the number of rows used.\n        // This means that the XP awarded decreases as the number of rows used increases.\n        val xpMultiplier = (2 / sqrt(rowsUsed.toDouble()))\n\n        return (defaultXp.toInt() * xpMultiplier).roundToUInt()\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/di/UserModule.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.di\n\nimport com.infinitepower.newquiz.core.user_services.LocalUserServiceImpl\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nabstract class UserModule {\n    @Binds\n    @Singleton\n    abstract fun bindUserService(\n        impl: LocalUserServiceImpl\n    ): UserService\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/di/XpGeneratorsModule.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.di\n\nimport com.infinitepower.newquiz.core.user_services.data.xp.ComparisonQuizXpGeneratorImpl\nimport com.infinitepower.newquiz.core.user_services.data.xp.MultiChoiceQuizXpGeneratorImpl\nimport com.infinitepower.newquiz.core.user_services.data.xp.WordleXpGeneratorImpl\nimport com.infinitepower.newquiz.core.user_services.domain.xp.ComparisonQuizXpGenerator\nimport com.infinitepower.newquiz.core.user_services.domain.xp.MultiChoiceQuizXpGenerator\nimport com.infinitepower.newquiz.core.user_services.domain.xp.WordleXpGenerator\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nabstract class XpGeneratorsModule {\n    @Binds\n    @Singleton\n    abstract fun bindMultiChoiceQuizXpGenerator(impl: MultiChoiceQuizXpGeneratorImpl): MultiChoiceQuizXpGenerator\n\n    @Binds\n    @Singleton\n    abstract fun bindWordleXpGenerator(impl: WordleXpGeneratorImpl): WordleXpGenerator\n\n    @Binds\n    @Singleton\n    abstract fun bindComparisonQuizXpGenerator(impl: ComparisonQuizXpGeneratorImpl): ComparisonQuizXpGenerator\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/domain/xp/ComparisonQuizXpGenerator.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.domain.xp\n\ninterface ComparisonQuizXpGenerator : XpGenerator {\n    fun getDefaultXpForAnswer(): UInt\n\n    /**\n     * Generates the XP for the user based on the [endPosition] they reached in the quiz\n     * and the number of [skippedAnswers], this number of [skippedAnswers]\n     * will be deducted from the [endPosition].\n     *\n     * If the [endPosition] is 1, the user has answered incorrectly, so no XP is awarded.\n     *\n     * @param endPosition The position the user reached in the quiz\n     * @param skippedAnswers The number of questions the user skipped\n     */\n    fun generateXp(\n        endPosition: UInt,\n        skippedAnswers: UInt,\n    ): UInt\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/domain/xp/MultiChoiceQuizXpGenerator.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.domain.xp\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\n\ninterface MultiChoiceQuizXpGenerator : XpGenerator {\n    fun getDefaultXpReward(): Map<QuestionDifficulty, UInt>\n\n    /**\n     * Generates a amount of XP based on the given question steps.\n     */\n    fun generateXp(\n        questionSteps: List<MultiChoiceQuestionStep.Completed>\n    ): UInt\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/domain/xp/WordleXpGenerator.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.domain.xp\n\ninterface WordleXpGenerator : XpGenerator {\n    fun getDefaultXp(): UInt\n\n    /**\n     * Calculates the XP awarded to the player for completing a game of Wordle.\n     *\n     * Currently it does not take into account the difficulty of the game, because\n     * the difficulty is not yet implemented in the game.\n     *\n     * The formula is:\n     *\n     *    xp = xpForDifficulty * (2 / sqrt(rowsUsed))\n     *\n     * @param rowsUsed The number of rows used in the game.\n     * @return The XP awarded to the player.\n     */\n    fun generateXp(rowsUsed: UInt): UInt\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/domain/xp/XpGenerator.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.domain.xp\n\ninterface XpGenerator\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/model/User.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.model\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.core.common.DEFAULT_USER_PHOTO\nimport com.infinitepower.newquiz.core.util.kotlin.div\nimport com.infinitepower.newquiz.core.util.kotlin.pow\nimport com.infinitepower.newquiz.core.util.kotlin.roundToUInt\nimport kotlin.math.floor\nimport kotlin.math.sqrt\n\n@Keep\ndata class User(\n    val uid: String,\n    val fullName: String = \"NewQuiz User\",\n    val imageUrl: String = DEFAULT_USER_PHOTO,\n    val totalXp: ULong = 0u,\n    val diamonds: UInt = 0u,\n) {\n    companion object {\n        private const val XP_FACTOR = 100\n    }\n\n    val level: UInt\n        get() = floor(sqrt(totalXp / XP_FACTOR.toDouble())).roundToUInt()\n\n    fun getNextLevelXp(): UInt = getRequiredXpForLevel(level + 1u)\n\n    private fun getRequiredXpForLevel(level: UInt): UInt = level.pow(2) * XP_FACTOR.toUInt()\n\n    fun getLevelProgress(): Float {\n        val currentLevelXp = getRequiredXpForLevel(level)\n        val nextLevelXp = getNextLevelXp()\n\n        val requiredXp = nextLevelXp - currentLevelXp\n        val currentXp = totalXp - currentLevelXp\n\n        return currentXp.toFloat() / requiredXp.toFloat()\n    }\n\n    fun getRequiredXP(): ULong = getNextLevelXp() - totalXp\n\n    fun isNewLevel(newXp: ULong): Boolean = getLevelAfter(newXp) > level\n\n    fun getLevelAfter(newXp: ULong): UInt = copy(totalXp = totalXp + newXp).level\n}\n"
  },
  {
    "path": "core/user-services/src/main/kotlin/com/infinitepower/newquiz/core/user_services/workers/MultiChoiceQuizEndGameWorker.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.workers\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.WorkerParameters\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\nimport kotlinx.serialization.json.Json\n\n@HiltWorker\nclass MultiChoiceQuizEndGameWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    private val analyticsHelper: AnalyticsHelper,\n    private val userService: UserService\n) : CoroutineWorker(appContext, workerParams) {\n    companion object {\n        const val INPUT_QUESTION_STEPS = \"QUESTION_STEPS\"\n        const val INPUT_GENERATE_XP = \"GENERATE_XP\"\n    }\n\n    override suspend fun doWork(): Result {\n        val questionStepsStr = inputData.getString(INPUT_QUESTION_STEPS) ?: return Result.failure()\n        val questionSteps: List<MultiChoiceQuestionStep.Completed> =\n            Json.decodeFromString(questionStepsStr)\n\n        val generateXp = inputData.getBoolean(INPUT_GENERATE_XP, true)\n\n        analyticsHelper.logEvent(\n            AnalyticsEvent.MultiChoiceGameEnd(\n                questionsSize = questionSteps.size,\n                correctAnswers = questionSteps.count { it.correct }\n            )\n        )\n\n        userService.saveMultiChoiceGame(\n            questionSteps = questionSteps,\n            generateXp = generateXp\n        )\n\n        return Result.success()\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/test/kotlin/com/infinitepower/newquiz/core/user_services/DateTimeRangeFormatterTest.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.TimestampWithXP\nimport kotlinx.datetime.Clock\nimport kotlinx.datetime.TimeZone\nimport kotlinx.datetime.atStartOfDayIn\nimport kotlinx.datetime.toLocalDateTime\nimport kotlin.test.Test\nimport kotlin.time.Duration.Companion.days\nimport kotlin.time.Duration.Companion.hours\n\ninternal class DateTimeRangeFormatterTest {\n    @Test\n    fun `test day formatter aggregateResults`() {\n        val now = Clock.System.now()\n\n        val results = listOf(\n            TimestampWithXP(timestamp = now.toEpochMilliseconds(), value = 10),\n            TimestampWithXP(timestamp = now.toEpochMilliseconds(), value = 5),\n            TimestampWithXP(timestamp = (now - 1.hours).toEpochMilliseconds(), value = 20),\n            TimestampWithXP(timestamp = (now - 6.hours).toEpochMilliseconds(), value = 30),\n            TimestampWithXP(timestamp = (now - 3.hours).toEpochMilliseconds(), value = 40),\n            TimestampWithXP(timestamp = (now - 1.days - 3.hours).toEpochMilliseconds(), value = 40),\n        )\n\n        val formatter = DateTimeRangeFormatter.Day\n        val resultsAggregated = formatter.aggregateResults(results)\n\n        assertThat(resultsAggregated).hasSize(4)\n    }\n\n    @Test\n    fun `test week formatter aggregateResults`() {\n        val now = Clock.System.now()\n\n        val results = listOf(\n            TimestampWithXP(timestamp = now.toEpochMilliseconds(), value = 10),\n            TimestampWithXP(timestamp = now.toEpochMilliseconds(), value = 5),\n            TimestampWithXP(timestamp = (now - 1.hours).toEpochMilliseconds(), value = 20),\n            TimestampWithXP(timestamp = (now - 32.hours).toEpochMilliseconds(), value = 30),\n            TimestampWithXP(timestamp = (now - 6.days).toEpochMilliseconds(), value = 40),\n        )\n\n        val formatter = DateTimeRangeFormatter.Week\n        val resultsAggregated = formatter.aggregateResults(results)\n\n        assertThat(resultsAggregated).hasSize(3)\n    }\n\n    @Test\n    fun `test getNowDateTimeRange`() {\n        val now = Clock.System.now()\n        val tz = TimeZone.currentSystemDefault()\n\n        val today = DateTimeRangeFormatter.Day.getNowDateTimeRange(now, tz)\n\n        val todayStart = today.first.toLocalDateTime(tz).date.atStartOfDayIn(tz)\n        assertThat(today.first).isEqualTo(todayStart)\n        assertThat(today.second).isEqualTo(now)\n\n        val thisWeek = DateTimeRangeFormatter.Week.getNowDateTimeRange(now, tz)\n\n        val thisWeekStart = now - 7.days\n        assertThat(thisWeek.first).isEqualTo(thisWeekStart)\n        assertThat(thisWeek.second).isEqualTo(now)\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/test/kotlin/com/infinitepower/newquiz/core/user_services/LocalUserServiceImplUnitTest.kt",
    "content": "package com.infinitepower.newquiz.core.user_services\n\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.PreferenceDataStoreFactory\nimport androidx.datastore.preferences.core.Preferences\nimport app.cash.turbine.test\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.database.model.user.ComparisonQuizGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.MultiChoiceGameResultEntity\nimport com.infinitepower.newquiz.core.database.model.user.WordleGameResultEntity\nimport com.infinitepower.newquiz.core.datastore.common.LocalUserCommon\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.PreferencesDatastoreManager\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.testing.domain.FakeGameResultDao\nimport com.infinitepower.newquiz.core.user_services.data.xp.ComparisonQuizXpGeneratorImpl\nimport com.infinitepower.newquiz.core.user_services.data.xp.MultiChoiceQuizXpGeneratorImpl\nimport com.infinitepower.newquiz.core.user_services.data.xp.WordleXpGeneratorImpl\nimport com.infinitepower.newquiz.core.user_services.domain.xp.ComparisonQuizXpGenerator\nimport com.infinitepower.newquiz.core.user_services.domain.xp.MultiChoiceQuizXpGenerator\nimport com.infinitepower.newquiz.core.user_services.domain.xp.WordleXpGenerator\nimport com.infinitepower.newquiz.core.user_services.model.User\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport io.mockk.coEvery\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.datetime.Clock\nimport kotlinx.datetime.TimeZone\nimport kotlinx.datetime.toLocalDateTime\nimport org.junit.jupiter.api.io.TempDir\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.CsvSource\nimport java.io.File\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\nimport kotlin.time.Duration.Companion.days\nimport kotlin.time.Duration.Companion.minutes\nimport kotlin.time.measureTimedValue\n\n/**\n * Tests for [LocalUserServiceImpl]\n */\ninternal class LocalUserServiceImplUnitTest {\n    @TempDir\n    lateinit var tmpDir: File\n\n    private lateinit var dataStoreManager: DataStoreManager\n\n    private val remoteConfig: RemoteConfig = mockk()\n    private lateinit var gameResultDao: GameResultDao\n\n    private val multiChoiceQuizXpGenerator: MultiChoiceQuizXpGenerator =\n        MultiChoiceQuizXpGeneratorImpl(remoteConfig)\n    private val wordleXpGenerator: WordleXpGenerator = WordleXpGeneratorImpl(remoteConfig)\n    private val comparisonQuizXpGenerator: ComparisonQuizXpGenerator =\n        ComparisonQuizXpGeneratorImpl(remoteConfig)\n\n    private lateinit var localUserServiceImpl: LocalUserServiceImpl\n\n    companion object {\n        private const val INITIAL_DIAMONDS = 10\n        private const val NEW_LEVEL_DIAMONDS = 10\n    }\n\n    @BeforeTest\n    fun setUp() {\n        val testDataStore: DataStore<Preferences> = PreferenceDataStoreFactory.create(\n            produceFile = { File(tmpDir, \"user.preferences_pb\") }\n        )\n\n        dataStoreManager = PreferencesDatastoreManager(testDataStore)\n\n        gameResultDao = FakeGameResultDao()\n\n        every { remoteConfig.get(RemoteConfigValue.WORDLE_DEFAULT_XP_REWARD) } returns 10\n        every { remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_DEFAULT_XP_REWARD) } returns 10\n        every { remoteConfig.get(RemoteConfigValue.MULTICHOICE_QUIZ_DEFAULT_XP_REWARD) } returns \"\"\"\n            {\n                \"easy\": 10,\n                \"medium\": 20,\n                \"hard\": 30\n            }\n        \"\"\".trimIndent()\n\n        localUserServiceImpl = LocalUserServiceImpl(\n            dataStoreManager = dataStoreManager,\n            remoteConfig = remoteConfig,\n            gameResultDao = gameResultDao,\n            multiChoiceXpGenerator = multiChoiceQuizXpGenerator,\n            wordleXpGenerator = wordleXpGenerator,\n            comparisonQuizXpGenerator = comparisonQuizXpGenerator\n        )\n    }\n\n    @Test\n    fun `userAvailable() should return true when uid is not blank`() = runTest {\n        dataStoreManager.editPreference(LocalUserCommon.UserUid.key, \"uid\")\n\n        val result = localUserServiceImpl.userAvailable()\n        assertThat(result).isTrue()\n    }\n\n    @Test\n    fun `getUserDiamonds() should return the user diamonds`() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        dataStoreManager.editPreference(\n            LocalUserCommon.UserDiamonds(INITIAL_DIAMONDS).key,\n            INITIAL_DIAMONDS\n        )\n\n        val result = localUserServiceImpl.getUserDiamonds()\n\n        assertThat(result).isEqualTo(INITIAL_DIAMONDS.toUInt())\n    }\n\n    @Test\n    fun `getUserDiamonds() should return the initial diamonds when the user diamonds are not set`() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n\n        val result = localUserServiceImpl.getUserDiamonds()\n\n        assertThat(result).isEqualTo(INITIAL_DIAMONDS.toUInt())\n    }\n\n    @Test\n    fun `getUserDiamondsFlow() should return the user diamonds`() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        dataStoreManager.editPreference(\n            LocalUserCommon.UserDiamonds(INITIAL_DIAMONDS).key,\n            INITIAL_DIAMONDS\n        )\n\n        localUserServiceImpl.getUserDiamondsFlow().test {\n            assertThat(awaitItem()).isEqualTo(INITIAL_DIAMONDS.toUInt())\n        }\n    }\n\n    @Test\n    fun `addRemoveDiamonds() should add diamonds to the user`() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        dataStoreManager.editPreference(\n            LocalUserCommon.UserDiamonds(INITIAL_DIAMONDS).key,\n            INITIAL_DIAMONDS\n        )\n\n        val diamondsToAdd = 5\n\n        localUserServiceImpl.addRemoveDiamonds(diamondsToAdd)\n\n        // Check that the diamonds were added\n        val expectedDiamonds = INITIAL_DIAMONDS + diamondsToAdd\n        val result = dataStoreManager.getPreference(LocalUserCommon.UserDiamonds(expectedDiamonds))\n        assertThat(result).isEqualTo(expectedDiamonds)\n    }\n\n    @Test\n    fun `updateNewLevelDiamonds() should update the user diamonds`() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        dataStoreManager.editPreference(\n            LocalUserCommon.UserDiamonds(INITIAL_DIAMONDS).key,\n            INITIAL_DIAMONDS\n        )\n\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        localUserServiceImpl.updateNewLevelDiamonds()\n\n        // Check that the diamonds were added\n        val expectedDiamonds = INITIAL_DIAMONDS + NEW_LEVEL_DIAMONDS\n        val result = dataStoreManager.getPreference(LocalUserCommon.UserDiamonds(expectedDiamonds))\n        assertThat(result).isEqualTo(expectedDiamonds)\n    }\n\n    @Test\n    fun `getUser() should return null when uid is blank`() = runTest {\n        dataStoreManager.editPreference(LocalUserCommon.UserUid.key, \"\")\n\n        val result = localUserServiceImpl.getUser()\n        assertThat(result).isNull()\n    }\n\n    @Test\n    fun `getUser() should return User when uid is not blank`() = runTest {\n        dataStoreManager.editPreference(LocalUserCommon.UserUid.key, \"uid\")\n        dataStoreManager.editPreference(LocalUserCommon.UserTotalXp.key, 0)\n\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        dataStoreManager.editPreference(\n            LocalUserCommon.UserDiamonds(INITIAL_DIAMONDS).key,\n            INITIAL_DIAMONDS\n        )\n\n        val result = localUserServiceImpl.getUser()\n\n        assertThat(result).isNotNull()\n\n        val expectedUser = User(\n            uid = \"uid\",\n            totalXp = 0u,\n            diamonds = INITIAL_DIAMONDS.toUInt()\n        )\n        assertThat(result).isEqualTo(expectedUser)\n    }\n\n    @CsvSource(\n        \"0\",\n        \"100\",\n        \"399\",\n        \"1000\"\n    )\n    @ParameterizedTest(name = \"test getNextLevelXp() when totalXp is {0}\")\n    fun `test saveMultiChoiceGame when generate xp is enabled`(\n        initialXp: Long\n    ) = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        dataStoreManager.editPreference(LocalUserCommon.UserTotalXp.key, initialXp)\n\n        // Average answer time is 8 seconds\n        val questionSteps = listOf(\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true,\n                questionTime = 10\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = false,\n                questionTime = 8\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true,\n                questionTime = 6\n            ),\n        )\n\n        val initialUser = localUserServiceImpl.getUser()\n        require(initialUser != null)\n\n        localUserServiceImpl.saveMultiChoiceGame(\n            questionSteps = questionSteps,\n            generateXp = true\n        )\n\n        // Check that the user's total xp has been updated\n        val updatedUser = localUserServiceImpl.getUser()\n        require(updatedUser != null)\n\n        val expectedXp = multiChoiceQuizXpGenerator.generateXp(questionSteps)\n\n        // Check if the new xp is in the range of the generated xp\n        val newXp = updatedUser.totalXp - initialUser.totalXp\n        assertThat(newXp.toInt()).isEqualTo(expectedXp.toInt())\n\n        // If the user is in new level, check if the diamonds have been updated\n        if (initialUser.isNewLevel(newXp)) {\n            assertThat(updatedUser.diamonds).isEqualTo(initialUser.diamonds + NEW_LEVEL_DIAMONDS.toUInt())\n        } else {\n            assertThat(updatedUser.diamonds).isEqualTo(initialUser.diamonds)\n        }\n\n        // Check if the game result has been saved\n        val gameResults = gameResultDao.getMultiChoiceResults()\n\n        assertThat(gameResults).hasSize(1)\n\n        // Because there is only one game result, we can assume that the first one is the one we want\n        gameResults.first().apply {\n            assertThat(correctAnswers).isEqualTo(questionSteps.count { it.correct })\n            assertThat(questionCount).isEqualTo(questionSteps.count())\n            assertThat(averageAnswerTime).isEqualTo(\n                questionSteps.map { it.questionTime }.average()\n            )\n            assertThat(earnedXp).isEqualTo(newXp.toInt())\n        }\n    }\n\n    @Test\n    fun `test saveMultiChoiceGame when generate xp is disabled`() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        // Average answer time is 8 seconds\n        val questionSteps = listOf(\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true,\n                questionTime = 10\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = false,\n                questionTime = 8\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true,\n                questionTime = 6\n            ),\n        )\n\n        val initialUser = localUserServiceImpl.getUser()\n        require(initialUser != null)\n\n        localUserServiceImpl.saveMultiChoiceGame(\n            questionSteps = questionSteps,\n            generateXp = false\n        )\n\n        val updatedUser = localUserServiceImpl.getUser()\n        require(updatedUser != null)\n\n        // because the xp generation is disabled, the new xp should be 0\n        val newXp = updatedUser.totalXp - initialUser.totalXp\n        assertThat(newXp).isEqualTo(0uL)\n\n        // Check if the game result has been saved\n        val gameResults = gameResultDao.getMultiChoiceResults()\n\n        assertThat(gameResults).hasSize(1)\n    }\n\n    @CsvSource(\n        \"0\",\n        \"100\",\n        \"399\",\n        \"1000\"\n    )\n    @ParameterizedTest(name = \"test saveWordleGame when generate xp is enabled and totalXp is {0}\")\n    fun `test saveWordleGame when generate xp is enabled`(\n        initialXp: Long\n    ) = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        dataStoreManager.editPreference(LocalUserCommon.UserTotalXp.key, initialXp)\n\n        val initialUser = localUserServiceImpl.getUser()\n        require(initialUser != null)\n\n        localUserServiceImpl.saveWordleGame(\n            wordLength = 5u,\n            rowsUsed = 3u,\n            maxRows = Int.MAX_VALUE,\n            categoryId = \"category\",\n            generateXp = true\n        )\n\n        // Check that the user's total xp has been updated\n        val updatedUser = localUserServiceImpl.getUser()\n        require(updatedUser != null)\n\n        val expectedXp = wordleXpGenerator.generateXp(rowsUsed = 3u)\n\n        // Check if the new xp is equal to the generated xp\n        val newXp = updatedUser.totalXp - initialUser.totalXp\n        assertThat(newXp.toInt()).isEqualTo(expectedXp.toInt())\n\n        // If the user is in new level, check if the diamonds have been updated\n        if (initialUser.isNewLevel(newXp)) {\n            assertThat(updatedUser.diamonds).isEqualTo(initialUser.diamonds + NEW_LEVEL_DIAMONDS.toUInt())\n        } else {\n            assertThat(updatedUser.diamonds).isEqualTo(initialUser.diamonds)\n        }\n\n        // Check if the game result has been saved\n        val gameResults = gameResultDao.getWordleResults()\n        assertThat(gameResults).hasSize(1)\n\n        // Because there is only one game result, we can assume that the first one is the one we want\n        gameResults.first().apply {\n            assertThat(wordLength).isEqualTo(5)\n            assertThat(rowsUsed).isEqualTo(3)\n            assertThat(maxRows).isEqualTo(Int.MAX_VALUE)\n            assertThat(earnedXp).isEqualTo(newXp.toInt())\n        }\n    }\n\n    @Test\n    fun `test saveWordleGame when generate xp is disabled`() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        val initialUser = localUserServiceImpl.getUser()\n        require(initialUser != null)\n\n        localUserServiceImpl.saveWordleGame(\n            wordLength = 5u,\n            rowsUsed = 3u,\n            maxRows = Int.MAX_VALUE,\n            categoryId = \"category\",\n            generateXp = false\n        )\n\n        // Check that the user's total xp has been updated\n        val updatedUser = localUserServiceImpl.getUser()\n        require(updatedUser != null)\n\n        // because the xp generation is disabled, the new xp should be 0\n        val newXp = updatedUser.totalXp - initialUser.totalXp\n        assertThat(newXp).isEqualTo(0uL)\n\n        // Check if the game result has been saved\n        val gameResults = gameResultDao.getWordleResults()\n        assertThat(gameResults).hasSize(1)\n    }\n\n    @CsvSource(\n        \"0\",\n        \"100\",\n        \"399\",\n        \"1000\"\n    )\n    @ParameterizedTest(name = \"test saveComparisonQuizGame when generate xp is enabled and totalXp is {0}\")\n    fun `test saveComparisonQuizGame when generate xp is enabled`(\n        initialXp: Long\n    ) = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        dataStoreManager.editPreference(LocalUserCommon.UserTotalXp.key, initialXp)\n\n        val initialUser = localUserServiceImpl.getUser()\n        require(initialUser != null)\n\n        val endPosition = 5u\n\n        localUserServiceImpl.saveComparisonQuizGame(\n            categoryId = \"category\",\n            comparisonMode = ComparisonMode.GREATER.name,\n            endPosition = endPosition,\n            skippedAnswers = 1u,\n            generateXp = true\n        )\n\n        // Check that the user's total xp has been updated\n        val updatedUser = localUserServiceImpl.getUser()\n        require(updatedUser != null)\n\n        val expectedXp = comparisonQuizXpGenerator.generateXp(\n            endPosition = endPosition,\n            skippedAnswers = 1u,\n        )\n\n        // Check if the new xp is equal to the generated xp\n        val newXp = updatedUser.totalXp - initialUser.totalXp\n        assertThat(newXp.toInt()).isEqualTo(expectedXp.toInt())\n\n        // If the user is in new level, check if the diamonds have been updated\n        if (initialUser.isNewLevel(newXp)) {\n            assertThat(updatedUser.diamonds).isEqualTo(initialUser.diamonds + NEW_LEVEL_DIAMONDS.toUInt())\n        } else {\n            assertThat(updatedUser.diamonds).isEqualTo(initialUser.diamonds)\n        }\n\n        // Check if the game result has been saved\n        val gameResults = gameResultDao.getComparisonQuizResults()\n        assertThat(gameResults).hasSize(1)\n\n        // Because there is only one game result, we can assume that the first one is the one we want\n        gameResults.first().apply {\n            assertThat(comparisonMode).isEqualTo(ComparisonMode.GREATER.name)\n            assertThat(this.endPosition).isEqualTo(endPosition.toInt())\n            assertThat(earnedXp).isEqualTo(newXp.toInt())\n        }\n    }\n\n    @Test\n    fun `test saveComparisonQuizGame when generate xp is disabled`() = runTest {\n        coEvery { remoteConfig.get(RemoteConfigValue.USER_INITIAL_DIAMONDS) } returns INITIAL_DIAMONDS\n        coEvery { remoteConfig.get(RemoteConfigValue.NEW_LEVEL_DIAMONDS) } returns NEW_LEVEL_DIAMONDS\n\n        val initialUser = localUserServiceImpl.getUser()\n        require(initialUser != null)\n\n        val endPosition = 5u\n\n        localUserServiceImpl.saveComparisonQuizGame(\n            categoryId = \"category\",\n            comparisonMode = ComparisonMode.GREATER.name,\n            endPosition = endPosition,\n            skippedAnswers = 0u,\n            generateXp = false\n        )\n\n        // Check that the user's total xp has been updated\n        val updatedUser = localUserServiceImpl.getUser()\n        require(updatedUser != null)\n\n        // because the xp generation is disabled, the new xp should be 0\n        val newXp = updatedUser.totalXp - initialUser.totalXp\n        assertThat(newXp).isEqualTo(0uL)\n\n        // Check if the game result has been saved\n        val gameResults = gameResultDao.getComparisonQuizResults()\n        assertThat(gameResults).hasSize(1)\n    }\n\n    @Test\n    fun `test getXpEarnedByRange() and getXpEarnedInLastDays()`() = runTest {\n        val now = Clock.System.now()\n\n        val startInstant = now - 7.days // 7 days ago\n\n        // Insert multi choice results\n        gameResultDao.insertMultiChoiceResult(\n            MultiChoiceGameResultEntity(\n                correctAnswers = 0,\n                skippedQuestions = 0,\n                questionCount = 0,\n                averageAnswerTime = 0.0,\n                earnedXp = 5,\n                playedAt = (now - 1.minutes).toEpochMilliseconds() // today\n            ),\n            MultiChoiceGameResultEntity(\n                correctAnswers = 0,\n                skippedQuestions = 0,\n                questionCount = 0,\n                averageAnswerTime = 0.0,\n                earnedXp = 10,\n                playedAt = (now - 2.minutes).toEpochMilliseconds() // today\n            ),\n            MultiChoiceGameResultEntity(\n                correctAnswers = 0,\n                skippedQuestions = 0,\n                questionCount = 0,\n                averageAnswerTime = 0.0,\n                earnedXp = 20,\n                playedAt = (now - 1.days).toEpochMilliseconds() // yesterday\n            ),\n            // Insert a result that is not in the current week\n            MultiChoiceGameResultEntity(\n                correctAnswers = 0,\n                skippedQuestions = 0,\n                questionCount = 0,\n                averageAnswerTime = 0.0,\n                earnedXp = 20,\n                playedAt = (now - 10.days).toEpochMilliseconds() // 10 days ago\n            ),\n        )\n\n        // Insert wordle results\n        gameResultDao.insertWordleResult(\n            WordleGameResultEntity(\n                earnedXp = 10,\n                playedAt = (now - 2.days).toEpochMilliseconds(), // before yesterday\n                wordLength = 5,\n                rowsUsed = 3,\n                maxRows = Int.MAX_VALUE,\n                categoryId = \"category\"\n            ),\n        )\n\n        // Insert comparison quiz results\n        gameResultDao.insertComparisonQuizResult(\n            ComparisonQuizGameResultEntity(\n                earnedXp = 10,\n                playedAt = (now - 4.minutes).toEpochMilliseconds(), // today\n                comparisonMode = ComparisonMode.GREATER.name,\n                endPosition = 5,\n                categoryId = \"category\",\n                skippedAnswers = 0\n            )\n        )\n\n        // Results:\n        // 3 results today\n        // 1 result yesterday\n        // 1 result before yesterday\n        // 1 result 10 days ago (not in the current week, should not be returned)\n        // 5 in total to be returned\n\n        // XP for days:\n        // today: 25\n        // yesterday: 20\n        // before yesterday: 10\n\n        // Check if the results are returned\n        val resultTimed = measureTimedValue {\n            localUserServiceImpl.getXpEarnedBy(\n                start = startInstant,\n                end = now\n            )\n        }\n\n        val lastWeek = DateTimeRangeFormatter.Week.getNowDateTimeRange(now)\n        val resultLast7Days = localUserServiceImpl.getXpEarnedBy(lastWeek)\n\n        val result = resultTimed.value\n\n        assertThat(result).isEqualTo(resultLast7Days)\n\n        assertThat(result).hasSize(5)\n\n        result.forEach { (date, _) ->\n            // Check if the result from the other week is not returned\n            assertThat(date).isAtLeast(startInstant.toEpochMilliseconds())\n            assertThat(date).isAtMost(now.toEpochMilliseconds())\n        }\n\n        // Test getXpEarnedByRangeFlow() because it is same as getXpEarnedByRange()\n        localUserServiceImpl.getXpEarnedByFlow(lastWeek).test {\n            assertThat(awaitItem()).isEqualTo(result)\n        }\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/test/kotlin/com/infinitepower/newquiz/core/user_services/data/xp/ComparisonQuizXpGeneratorImplTest.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.data.xp\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport io.mockk.every\nimport io.mockk.mockk\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.CsvSource\nimport kotlin.test.BeforeTest\n\n/**\n * Tests for [ComparisonQuizXpGeneratorImpl].\n */\ninternal class ComparisonQuizXpGeneratorImplTest {\n    private lateinit var comparisonQuizXpGeneratorImpl: ComparisonQuizXpGeneratorImpl\n\n    private val remoteConfig: RemoteConfig = mockk()\n\n    @BeforeTest\n    fun setUp() {\n        comparisonQuizXpGeneratorImpl = ComparisonQuizXpGeneratorImpl(remoteConfig)\n\n        every { remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_DEFAULT_XP_REWARD) } returns 10\n    }\n\n    @ParameterizedTest(name = \"test getXpForPosition with endPosition = {0} and skippedAnswers = {1}\")\n    @CsvSource(\"\"\"\n        1, 0\n    \"\"\")\n    fun `test getXpForPosition`(\n        endPosition: Int,\n        skippedAnswers: Int\n    ) {\n        val xp = comparisonQuizXpGeneratorImpl.generateXp(\n            endPosition = endPosition.toUInt(),\n            skippedAnswers = 0u\n        )\n\n        val answersNotSkipped = endPosition - skippedAnswers - 1\n        val expectedXp = comparisonQuizXpGeneratorImpl.getDefaultXpForAnswer() * answersNotSkipped.toUInt()\n\n        assertThat(xp).isEqualTo(expectedXp)\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/test/kotlin/com/infinitepower/newquiz/core/user_services/data/xp/MultiChoiceQuizXpGeneratorImplTest.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.data.xp\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.SelectedAnswer\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlin.random.Random\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [MultiChoiceQuizXpGeneratorImpl].\n */\ninternal class MultiChoiceQuizXpGeneratorImplTest {\n    private lateinit var multiChoiceQuizXpGeneratorImpl: MultiChoiceQuizXpGeneratorImpl\n\n    private val remoteConfig: RemoteConfig = mockk()\n\n    @BeforeTest\n    fun setUp() {\n        every { remoteConfig.get(RemoteConfigValue.MULTICHOICE_QUIZ_DEFAULT_XP_REWARD) } returns \"\"\"\n            {\n                \"easy\": 10,\n                \"medium\": 20,\n                \"hard\": 30\n            }\n        \"\"\".trimIndent()\n\n        multiChoiceQuizXpGeneratorImpl = MultiChoiceQuizXpGeneratorImpl(\n            remoteConfig = remoteConfig\n        )\n    }\n\n    @Test\n    fun `getDefaultXpReward should return the default XP reward`() {\n        val defaultXpReward = multiChoiceQuizXpGeneratorImpl.getDefaultXpReward()\n\n        assertThat(defaultXpReward).isEqualTo(\n            mapOf(\n                QuestionDifficulty.Easy to 10u,\n                QuestionDifficulty.Medium to 20u,\n                QuestionDifficulty.Hard to 30u\n            )\n        )\n    }\n\n    @Test\n    fun `generateXp when all question steps are incorrect, should return 0`() {\n        val questionSteps = List(5) {\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = false,\n                selectedAnswer = SelectedAnswer.NONE\n            )\n        }\n\n        val randomXp = multiChoiceQuizXpGeneratorImpl.generateXp(questionSteps)\n\n        assertThat(randomXp).isEqualTo(0u)\n    }\n\n    @Test\n    fun `test generateXp`() {\n        val questionSteps = List(5) {\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = Random.nextBoolean(),\n                selectedAnswer = SelectedAnswer.NONE\n            )\n        // Add a skipped question step to test that it is not counted\n        } + MultiChoiceQuestionStep.Completed(\n            question = getBasicMultiChoiceQuestion(),\n            correct = true,\n            selectedAnswer = SelectedAnswer.NONE,\n            skipped = true\n        )\n\n        val generatedXp = multiChoiceQuizXpGeneratorImpl.generateXp(questionSteps)\n\n        val defaultXpReward = multiChoiceQuizXpGeneratorImpl.getDefaultXpReward()\n\n        val expectedXp = questionSteps\n            .filter { step ->\n                step.correct && !step.skipped\n            }.sumOf { step ->\n                val difficulty = step.question.difficulty\n\n                defaultXpReward[difficulty] ?: 0u\n            }\n\n        assertThat(generatedXp).isEqualTo(expectedXp)\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/test/kotlin/com/infinitepower/newquiz/core/user_services/data/xp/WordleXpGeneratorImplTest.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.data.xp\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.util.kotlin.roundToUInt\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlin.math.sqrt\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [WordleXpGeneratorImpl].\n */\ninternal class WordleXpGeneratorImplTest {\n    private lateinit var wordleXpGeneratorImpl: WordleXpGeneratorImpl\n\n    private val remoteConfig: RemoteConfig = mockk()\n\n    @BeforeTest\n    fun setUp() {\n        wordleXpGeneratorImpl = WordleXpGeneratorImpl(remoteConfig)\n\n        every { remoteConfig.get(RemoteConfigValue.WORDLE_DEFAULT_XP_REWARD) } returns 10\n    }\n\n    @Test\n    fun `test generateXp`() {\n        val rowsUsed = 5u\n\n        val generatedXp = wordleXpGeneratorImpl.generateXp(rowsUsed)\n\n        val defaultXp = wordleXpGeneratorImpl.getDefaultXp()\n        val expectedXp = (defaultXp.toInt() * (2 / sqrt(rowsUsed.toDouble()))).roundToUInt()\n\n        assertThat(generatedXp).isEqualTo(expectedXp)\n    }\n}\n"
  },
  {
    "path": "core/user-services/src/test/kotlin/com/infinitepower/newquiz/core/user_services/model/UserTest.kt",
    "content": "package com.infinitepower.newquiz.core.user_services.model\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.CsvSource\nimport kotlin.test.Test\n\n/**\n * Tests for [User].\n */\ninternal class UserTest {\n    private fun getTestUser(\n        uid: String = \"testUid\",\n        fullName: String = \"testFullName\",\n        imageUrl: String = \"testImageUrl\",\n        totalXp: ULong = 0u,\n        diamonds: UInt = 0u,\n    ) = User(\n        uid = uid,\n        fullName = fullName,\n        imageUrl = imageUrl,\n        totalXp = totalXp,\n        diamonds = diamonds,\n    )\n\n    @CsvSource(\n        \"0, 0\",\n        \"99, 0\",\n        \"100, 1\",\n        \"101, 1\",\n        \"9999, 9\",\n        \"10000, 10\",\n        \"10001, 10\",\n        \"1000000, 100\",\n        \"1000001, 100\",\n    )\n    @ParameterizedTest(name = \"totalXp = {0} -> level = {1}\")\n    fun `test user level`(\n        totalXp: Long,\n        expectedLevel: Int,\n    ) {\n        val user = getTestUser(totalXp = totalXp.toULong())\n\n        assertThat(user.level).isEqualTo(expectedLevel.toUInt())\n    }\n\n    @Test\n    fun `test get next level xp`() {\n        val user = getTestUser(totalXp = 0u)\n\n        // For level 0, next level xp is 100\n        assertThat(user.getNextLevelXp()).isEqualTo(100u)\n    }\n\n    @CsvSource(\n        \"0, 0.0\",\n        \"50, 0.5\",\n        \"99, 0.99\",\n        \"100, 0.0\", // New level 1\n        \"399, 0.99\",\n        \"400, 0.0\" // New level 2\n    )\n    @ParameterizedTest(name = \"totalXp = {0} should have progress = {1}\")\n    fun `test get level progress`(\n        totalXp: Long,\n        expectedProgress: Float,\n    ) {\n        val user = getTestUser(totalXp = totalXp.toULong())\n\n        assertThat(user.getLevelProgress()).isWithin(0.01f).of(expectedProgress)\n    }\n\n    @Test\n    fun `test get required xp`() {\n        val user = getTestUser(totalXp = 0u)\n\n        // For level 0, next level xp is 100\n        assertThat(user.getRequiredXP()).isEqualTo(100uL)\n\n        val user2 = getTestUser(totalXp = 100u)\n\n        // For level 1, next level xp is 400, so required xp is 300\n        assertThat(user2.getRequiredXP()).isEqualTo(300uL)\n    }\n\n    @Test\n    fun `test is new level`() {\n        val user = getTestUser(totalXp = 500u) // Level 2\n\n        assertThat(user.isNewLevel(100u)).isFalse()\n        assertThat(user.isNewLevel(400u)).isTrue()\n        assertThat(user.isNewLevel(500u)).isTrue()\n    }\n\n    @Test\n    fun `test get level after`() {\n        val user = getTestUser(totalXp = 500u) // Level 2\n\n        assertThat(user.getLevelAfter(100u)).isEqualTo(2u) // No level up\n        assertThat(user.getLevelAfter(400u)).isEqualTo(3u)\n        assertThat(user.getLevelAfter(500u)).isEqualTo(3u)\n    }\n}\n"
  },
  {
    "path": "data/.gitignore",
    "content": "/build"
  },
  {
    "path": "data/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library.compose)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n    id(\"com.google.devtools.ksp\")\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.data\"\n}\n\ndependencies {\n    implementation(libs.androidx.core.ktx)\n\n    implementation(libs.kotlinx.coroutines.playServices)\n\n    androidTestImplementation(libs.androidx.test.runner)\n    androidTestImplementation(libs.androidx.test.rules)\n    androidTestImplementation(libs.androidx.compose.ui.test)\n\n    implementation(libs.ktor.client.core)\n    implementation(libs.ktor.client.okhttp)\n    implementation(libs.ktor.client.serialization)\n\n    implementation(libs.hilt.navigationCompose)\n    ksp(libs.hilt.ext.compiler)\n    implementation(libs.hilt.ext.work)\n\n    implementation(libs.androidx.work.ktx)\n    androidTestImplementation(libs.androidx.work.testing)\n\n    implementation(libs.kotlinx.datetime)\n\n    implementation(projects.core)\n    implementation(projects.core.analytics)\n    implementation(projects.core.database)\n    implementation(projects.core.datastore)\n    implementation(projects.core.remoteConfig)\n    implementation(projects.core.userServices)\n    implementation(projects.domain)\n    implementation(projects.model)\n    testImplementation(projects.core.testing)\n    androidTestImplementation(projects.core.testing)\n}\n"
  },
  {
    "path": "data/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "data/proguard-rules.pro",
    "content": "# Keep `Companion` object fields of serializable classes.\n# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.\n-if @kotlinx.serialization.Serializable class **\n-keepclassmembers class <1> {\n    static <1>$Companion Companion;\n}\n\n# Keep `serializer()` on companion objects (both default and named) of serializable classes.\n-if @kotlinx.serialization.Serializable class ** {\n    static **$* *;\n}\n-keepclassmembers class <2>$<3> {\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n    public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n    public static <1> INSTANCE;\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.\n-keepattributes RuntimeVisibleAnnotations,AnnotationDefault\n\n-keepclassmembers @kotlinx.serialization.Serializable class ** {\n    *** Companion;\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n    public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n    public static <1> INSTANCE;\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n-keep class kotlin.reflect.** { *; }\n-dontwarn kotlin.reflect.**\n-keep class org.jetbrains.** { *; }\n\n# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.\n-keepattributes RuntimeVisibleAnnotations,AnnotationDefault\n\n-dontwarn java.lang.invoke.StringConcatFactory\n\n-dontwarn kotlin.**\n-dontwarn org.w3c.dom.events.*\n-dontwarn org.jetbrains.kotlin.di.InjectorForRuntimeDescriptorLoader\n\n-keepattributes SourceFile,LineNumberTable\n\n-keep class kotlin.** { *; }\n#-keep class kotlin.reflect.** { *; }\n#-keep class org.jetbrains.kotlin.** { *; }\n\n-keepclassmembers,allowoptimization enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n    **[] $VALUES;\n    public *;\n}\n\n-keepattributes InnerClasses\n\n# Ktor\n-keep class io.ktor.** { *; }\n-keep class kotlinx.coroutines.** { *; }\n-dontwarn kotlinx.atomicfu.**\n-dontwarn io.netty.**\n-dontwarn com.typesafe.**\n-dontwarn org.slf4j.**\n\n-keepattributes *Annotation*, InnerClasses\n-dontnote kotlinx.serialization.SerializationKt\n-keep,includedescriptorclasses class com.infinitepower.newsocial.compose.**$$serializer { *; }\n\n-keep class kotlin.reflect.** { *; }\n-dontwarn kotlin.reflect.**\n-keep class org.jetbrains.** { *; }"
  },
  {
    "path": "data/src/androidTest/AndroidManifest.xml",
    "content": "<manifest\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <application>\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            tools:node=\"remove\" />\n    </application>\n</manifest>"
  },
  {
    "path": "data/src/androidTest/java/com/infinitepower/newquiz/data/daily_challenge/DailyChallengeRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.daily_challenge\n\nimport android.content.Context\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.dao.DailyChallengeDao\nimport com.infinitepower.newquiz.data.repository.daily_challenge.util.getTitle\nimport com.infinitepower.newquiz.data.util.mappers.comparisonquiz.toEntity\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.datetime.Clock\nimport org.junit.Assert.assertThrows\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport javax.inject.Inject\nimport kotlin.time.Duration.Companion.days\n\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\nclass DailyChallengeRepositoryImplTest {\n\n    @get:Rule\n    val hiltRule = HiltAndroidRule(this)\n\n    private lateinit var context: Context\n\n    @Inject lateinit var dailyChallengeDao: DailyChallengeDao\n\n    @Inject lateinit var dailyChallengeRepository: DailyChallengeRepository\n\n    @Inject lateinit var comparisonQuizRepository: ComparisonQuizRepository\n\n    @Before\n    fun setup() {\n        context = InstrumentationRegistry.getInstrumentation().context\n\n        hiltRule.inject()\n    }\n\n    @Test\n    fun test_getDailyChallengeTasks_returnsCorrectTasks() = runTest {\n        val now = Clock.System.now()\n\n        val type1 = GameEvent.MultiChoice.PlayQuestions\n\n        val initialTasks = listOf(\n            DailyChallengeTask(\n                id = 1,\n                diamondsReward = 1u,\n                experienceReward = 1u,\n                isClaimed = false,\n                dateRange = (now - 1.days).rangeTo(now + 1.days), // Not expired\n                currentValue = 0u, // Not completed\n                maxValue = 1u,\n                event = type1,\n                title = type1.getTitle(1, comparisonQuizRepository.getCategories())\n            )\n        )\n\n        dailyChallengeDao.insertAll(initialTasks.map(DailyChallengeTask::toEntity))\n\n        val tasks = dailyChallengeRepository.getAvailableTasks()\n\n        assertThat(tasks).containsExactlyElementsIn(initialTasks)\n    }\n\n    @Test\n    fun test_checkAndGenerateTasks_shouldGenerateNewTasks() = runTest {\n        val now = Clock.System.now()\n\n        val type1 = GameEvent.MultiChoice.PlayQuestions\n        val task1 = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = (now - 2.days).rangeTo(now - 1.days), // Expired\n            currentValue = 0u, // Not completed\n            maxValue = 1u,\n            event = type1,\n            title = type1.getTitle(1, comparisonQuizRepository.getCategories())\n        )\n\n        val initialTasks = setOf(task1)\n\n        dailyChallengeDao.insertAll(initialTasks.map(DailyChallengeTask::toEntity))\n\n        dailyChallengeRepository.checkAndGenerateTasksIfNeeded(tasksToGenerate = 2)\n\n        val tasks = dailyChallengeRepository.getAllTasks().toSet()\n\n        assertThat(tasks).isNotEmpty()\n        assertThat(tasks).hasSize(3)\n\n        val newTasks = tasks - initialTasks\n\n        assertThat(newTasks).isNotEmpty()\n        assertThat(newTasks).hasSize(2)\n\n        val nowVerify = Clock.System.now()\n\n        // Check that the new tasks are not expired\n        newTasks.forEach { task ->\n            assertThat(nowVerify).isAtLeast(task.dateRange.start)\n            assertThat(nowVerify).isAtMost(task.dateRange.endInclusive)\n\n            println(task)\n        }\n    }\n\n    @Test\n    fun test_checkAndGenerateTasks_shouldGenerateNewTasks_whenInitialTasksAreEmpty() = runTest {\n        val initialTasks = emptySet<DailyChallengeTask>()\n\n        dailyChallengeRepository.checkAndGenerateTasksIfNeeded(tasksToGenerate = 2)\n\n        val tasks = dailyChallengeRepository.getAvailableTasks()\n\n        assertThat(tasks).isNotEmpty()\n        assertThat(tasks).hasSize(2)\n\n        val newTasks = tasks - initialTasks\n\n        assertThat(newTasks).isNotEmpty()\n        assertThat(newTasks).hasSize(2)\n\n        val now = Clock.System.now()\n\n        // Check that the new tasks are not expired\n        newTasks.forEach { task ->\n            assertThat(task.dateRange.contains(now)).isTrue()\n            assertThat(task.isActive()).isTrue()\n            assertThat(task.isCompleted()).isFalse()\n            assertThat(task.isExpired()).isFalse()\n            assertThat(task.isClaimed).isFalse()\n        }\n    }\n\n    @Test\n    fun test_completeTaskStep_shouldCompleteTheTask_ifTheCurrentValueIsEqualToTheMaxValue() = runTest {\n        val now = Clock.System.now()\n\n        val initialTasks = setOf(\n            DailyChallengeTask(\n                id = 1,\n                diamondsReward = 1u,\n                experienceReward = 1u,\n                isClaimed = false,\n                dateRange = (now - 1.days).rangeTo(now + 1.days), // Not expired\n                currentValue = 0u, // Not completed\n                maxValue = 1u,\n                event = GameEvent.MultiChoice.PlayQuestions,\n                title = GameEvent.MultiChoice.PlayQuestions.getTitle(1, comparisonQuizRepository.getCategories())\n            ),\n            DailyChallengeTask(\n                id = 2,\n                diamondsReward = 1u,\n                experienceReward = 1u,\n                isClaimed = false,\n                dateRange = (now - 1.days).rangeTo(now + 1.days), // Not expired\n                currentValue = 0u, // Not completed\n                maxValue = 1u,\n                event = GameEvent.MultiChoice.GetAnswersCorrect,\n                title = GameEvent.MultiChoice.GetAnswersCorrect.getTitle(1, comparisonQuizRepository.getCategories())\n            )\n        )\n\n        dailyChallengeDao.insertAll(initialTasks.map(DailyChallengeTask::toEntity))\n\n        // Check that the initial tasks are not completed\n        assertThat(dailyChallengeRepository.getAvailableTasks()).containsExactlyElementsIn(initialTasks)\n\n        // Complete the task\n        dailyChallengeRepository.completeTaskStep(GameEvent.MultiChoice.PlayQuestions)\n\n        val tasks = dailyChallengeRepository.getAvailableTasks()\n\n        assertThat(tasks).isNotEmpty()\n        assertThat(tasks).hasSize(2)\n\n        val completedTask = tasks.find { it.event == GameEvent.MultiChoice.PlayQuestions }\n\n        // Check that the task is not null\n        assertThat(completedTask).isNotNull()\n        if (completedTask == null) throw AssertionError(\"Completed task is null\")\n\n        assertThat(initialTasks).doesNotContain(completedTask)\n        assertThat(completedTask.currentValue).isEqualTo(completedTask.maxValue)\n        assertThat(completedTask.isCompleted()).isTrue()\n\n        // Check that the others task are not changed\n        val otherTasks = tasks - completedTask\n        assertThat(otherTasks).containsExactlyElementsIn(initialTasks.filter { it.id != completedTask.id })\n    }\n\n    @Test\n    fun completeTaskStep_shouldThrowException_ifTaskIsExpired() {\n        val now = Clock.System.now()\n\n        val task1 = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = (now - 2.days).rangeTo(now - 1.days), // Expired\n            currentValue = 0u, // Not completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = GameEvent.MultiChoice.PlayQuestions.getTitle(1, comparisonQuizRepository.getCategories())\n        )\n\n        runTest { dailyChallengeDao.insertAll(task1.toEntity()) }\n\n        val e = assertThrows(IllegalStateException::class.java) {\n            runTest {\n                dailyChallengeRepository.completeTaskStep(GameEvent.MultiChoice.PlayQuestions)\n            }\n        }\n\n        assertThat(e)\n            .hasMessageThat()\n            .isEqualTo(\"Task (${task1.title}) is expired.\")\n    }\n\n    @Test\n    fun completeTaskStep_shouldThrowException_ifTaskIsClaimed() {\n        val now = Clock.System.now()\n\n        val task1 = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = true,\n            dateRange = (now - 1.days).rangeTo(now + 1.days), // Not expired\n            currentValue = 0u, // Not completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = GameEvent.MultiChoice.PlayQuestions.getTitle(1, comparisonQuizRepository.getCategories())\n        )\n\n        runTest { dailyChallengeDao.insertAll(task1.toEntity()) }\n\n        val e = assertThrows(IllegalStateException::class.java) {\n            runTest {\n                dailyChallengeRepository.completeTaskStep(GameEvent.MultiChoice.PlayQuestions)\n            }\n        }\n\n        assertThat(e)\n            .hasMessageThat()\n            .isEqualTo(\"Task (${task1.title}) is already claimed.\")\n    }\n\n    @Test\n    fun completeTaskStep_shouldThrowException_ifTaskIsNotFound() {\n        val e = assertThrows(NullPointerException::class.java) {\n            runTest {\n                dailyChallengeRepository.completeTaskStep(GameEvent.MultiChoice.GetAnswersCorrect)\n            }\n        }\n\n        assertThat(e)\n            .hasMessageThat()\n            .isEqualTo(\"Task not found.\")\n    }\n}"
  },
  {
    "path": "data/src/androidTest/java/com/infinitepower/newquiz/data/repository/comparison_quiz/ComparisonQuizRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.comparison_quiz\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.database.model.user.ComparisonQuizGameResultEntity\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.datetime.Clock\nimport org.junit.Rule\nimport org.junit.runner.RunWith\nimport javax.inject.Inject\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\nimport kotlin.time.Duration.Companion.minutes\n\n/**\n * Tests for [ComparisonQuizRepositoryImpl]\n */\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\ninternal class ComparisonQuizRepositoryImplTest {\n    @get:Rule\n    val hiltRule = HiltAndroidRule(this)\n\n    @Inject lateinit var remoteConfig: RemoteConfig\n\n    @Inject lateinit var gameResultDao: GameResultDao\n\n    @Inject lateinit var comparisonQuizApi: ComparisonQuizApi\n\n    private lateinit var repository: ComparisonQuizRepositoryImpl\n\n    @BeforeTest\n    fun setUp() {\n        hiltRule.inject()\n\n        repository = ComparisonQuizRepositoryImpl(\n            remoteConfig = remoteConfig,\n            gameResultDao = gameResultDao,\n            comparisonQuizApi = comparisonQuizApi,\n        )\n    }\n\n    @Test\n    fun getHighestPosition_returnsHighestPosition() = runTest {\n        // The highest position is not stored in the database\n        // So it should return 0\n        val noHighestPosition = repository.getHighestPosition(categoryId = \"category\")\n        assertThat(noHighestPosition).isEqualTo(0)\n\n        val now = Clock.System.now()\n\n        gameResultDao.insertComparisonQuizResult(\n            ComparisonQuizGameResultEntity(\n                earnedXp = 10,\n                playedAt = (now - 4.minutes).toEpochMilliseconds(), // today\n                comparisonMode = ComparisonMode.GREATER.name,\n                endPosition = 5,\n                categoryId = \"category\",\n                skippedAnswers = 0\n            )\n        )\n\n        val highestPosition = repository.getHighestPosition(categoryId = \"category\")\n\n        assertThat(highestPosition).isEqualTo(5)\n    }\n}"
  },
  {
    "path": "data/src/androidTest/java/com/infinitepower/newquiz/data/repository/country/CountryRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.country\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport androidx.test.platform.app.InstrumentationRegistry\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport org.junit.runner.RunWith\nimport kotlin.test.Test\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class CountryRepositoryImplTest {\n    @Test\n    fun testGetCountries() = runTest {\n        val context = InstrumentationRegistry.getInstrumentation().targetContext\n\n        val remoteConfig = mockk<RemoteConfig>()\n\n        every { remoteConfig.get<String>(RemoteConfigValue.FLAG_BASE_URL) } returns \"local\"\n\n        val repository = CountryRepositoryImpl(\n            context = context,\n            remoteConfig = remoteConfig\n        )\n\n        val countries = repository.getAllCountries().onEach {\n            println(it)\n        }\n\n        assertThat(countries).isNotEmpty()\n    }\n}\n"
  },
  {
    "path": "data/src/androidTest/java/com/infinitepower/newquiz/data/worker/daily_challenge/VerifyDailyChallengeWorkerTest.kt",
    "content": "package com.infinitepower.newquiz.data.worker.daily_challenge\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorkerFactory\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.work.ListenableWorker\nimport androidx.work.testing.TestListenableWorkerBuilder\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.AppDatabase\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport kotlinx.coroutines.test.runTest\nimport org.junit.After\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport javax.inject.Inject\n\n@SmallTest\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\ninternal class VerifyDailyChallengeWorkerTest {\n    @get:Rule\n    val hiltRule = HiltAndroidRule(this)\n\n    @Inject lateinit var appDatabase: AppDatabase\n\n    @Inject lateinit var workerFactory: HiltWorkerFactory\n\n    @Inject lateinit var dailyChallengeRepository: DailyChallengeRepository\n\n    private lateinit var context: Context\n\n    @Before\n    fun setup() {\n        hiltRule.inject()\n\n        context = InstrumentationRegistry.getInstrumentation().context\n    }\n\n    @After\n    fun tearDown() {\n        appDatabase.close()\n    }\n\n    @Test\n    fun testPeriodicWork() = runTest {\n        // Clear the tasks.\n        dailyChallengeRepository.resetTasks()\n\n        val verifyDailyChallengeWorker = TestListenableWorkerBuilder<VerifyDailyChallengeWorker>(context)\n            .setWorkerFactory(workerFactory)\n            .build()\n\n        val result = verifyDailyChallengeWorker.doWork()\n\n        assertThat(result).isNotNull()\n        assertThat(result).isEqualTo(ListenableWorker.Result.success())\n\n        val tasks = dailyChallengeRepository.getAvailableTasks()\n        assertThat(tasks).isNotEmpty()\n    }\n}"
  },
  {
    "path": "data/src/androidTest/java/com/infinitepower/newquiz/data/worker/maze/CleanMazeQuizWorkerTest.kt",
    "content": "package com.infinitepower.newquiz.data.worker.maze\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorkerFactory\nimport androidx.test.core.app.ApplicationProvider\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.work.ListenableWorker\nimport androidx.work.testing.TestListenableWorkerBuilder\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.dao.MazeQuizDao\nimport com.infinitepower.newquiz.core.database.model.MazeQuizItemEntity\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Rule\nimport org.junit.runner.RunWith\nimport javax.inject.Inject\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\ninternal class CleanMazeQuizWorkerTest {\n    @get:Rule\n    val hiltRule = HiltAndroidRule(this)\n\n    @Inject lateinit var workerFactory: HiltWorkerFactory\n\n    @Inject lateinit var mazeQuizDao: MazeQuizDao\n\n    private lateinit var context: Context\n\n    @BeforeTest\n    fun setup() {\n        hiltRule.inject()\n\n        context = ApplicationProvider.getApplicationContext()\n    }\n\n    @Test\n    fun testCleanMazeQuizWorker() = runTest {\n        val mazeQuizItems = List(10) {\n            MazeQuizItemEntity(\n                difficulty = QuestionDifficulty.Easy,\n                played = false,\n                type = MazeQuizItemEntity.Type.MULTI_CHOICE,\n                mazeSeed = 0\n            )\n        }\n\n        mazeQuizDao.insertItems(mazeQuizItems)\n\n        // Check if the items were inserted correctly.\n        val mazeQuizItemsBeforeClean = mazeQuizDao.getAllMazeItems()\n        assertThat(mazeQuizItemsBeforeClean).hasSize(10)\n\n        // Clean the maze quiz items.\n        val cleanSavedMazeRequest = TestListenableWorkerBuilder<CleanMazeQuizWorker>(context)\n            .setWorkerFactory(workerFactory)\n            .build()\n\n        val result = cleanSavedMazeRequest.doWork()\n\n        assertThat(result).isNotNull()\n        assertThat(result).isEqualTo(ListenableWorker.Result.success())\n\n        // Check if the items were deleted correctly.\n        val mazeQuizItemsAfterClean = mazeQuizDao.getAllMazeItems()\n        assertThat(mazeQuizItemsAfterClean).isEmpty()\n    }\n}\n"
  },
  {
    "path": "data/src/androidTest/java/com/infinitepower/newquiz/data/worker/maze/GenerateMazeQuizWorkerTest.kt",
    "content": "package com.infinitepower.newquiz.data.worker.maze\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorkerFactory\nimport androidx.test.core.app.ApplicationProvider\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.work.ListenableWorker\nimport androidx.work.testing.TestListenableWorkerBuilder\nimport androidx.work.workDataOf\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.AppDatabase\nimport com.infinitepower.newquiz.core.database.dao.MazeQuizDao\nimport com.infinitepower.newquiz.domain.repository.wordle.WordleRepository\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Rule\nimport org.junit.runner.RunWith\nimport javax.inject.Inject\nimport kotlin.test.AfterTest\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Test for [GenerateMazeQuizWorker].\n */\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\ninternal class GenerateMazeQuizWorkerTest {\n    @get:Rule\n    val hiltRule = HiltAndroidRule(this)\n\n    @Inject\n    lateinit var workerFactory: HiltWorkerFactory\n\n    @Inject\n    lateinit var mazeQuizDao: MazeQuizDao\n\n    @Inject\n    lateinit var appDatabase: AppDatabase\n\n    @Inject\n    lateinit var wordleRepository: WordleRepository\n\n    private lateinit var context: Context\n\n    @BeforeTest\n    fun setup() {\n        hiltRule.inject()\n\n        context = ApplicationProvider.getApplicationContext()\n    }\n\n    @AfterTest\n    fun tearDown() {\n        appDatabase.close()\n    }\n\n    @Test\n    fun testGenerateMazeQuizWorker() = runTest {\n        val seed = 0\n        val questionSize = 50\n\n        val multiChoiceCategories = GenerateMazeQuizWorker.GameModes.MultiChoice.categories\n\n        val multiChoiceCategoriesId = multiChoiceCategories.map { category ->\n            category.id\n        }.toTypedArray()\n\n        val wordleCategories = GenerateMazeQuizWorker.GameModes.Wordle.categories\n\n        val wordleCategoriesIds = wordleCategories.map { category ->\n            category.id\n        }.toTypedArray()\n\n        val generateMazeQuizRequest = TestListenableWorkerBuilder<GenerateMazeQuizWorker>(context)\n            .setWorkerFactory(workerFactory)\n            .setInputData(\n                workDataOf(\n                    GenerateMazeQuizWorker.INPUT_SEED to seed,\n                    GenerateMazeQuizWorker.INPUT_MULTI_CHOICE_CATEGORIES to multiChoiceCategoriesId,\n                    GenerateMazeQuizWorker.INPUT_WORDLE_QUIZ_TYPES to wordleCategoriesIds,\n                    GenerateMazeQuizWorker.INPUT_QUESTION_SIZE to questionSize\n                )\n            ).build()\n\n        val result = generateMazeQuizRequest.doWork()\n\n        // Check if the worker finished successfully.\n        assertThat(result).isNotNull()\n        assertThat(result).isEqualTo(ListenableWorker.Result.success())\n\n        // Check if the items were inserted correctly.\n        val mazeQuizItems = mazeQuizDao.getAllMazeItems()\n\n        val allCategoryCount = multiChoiceCategories.count() + wordleCategories.count()\n        val questionSizePerMode = questionSize / allCategoryCount\n        // Get the real generated question size.\n        // The real generated question size may be different from the input question size\n        // Because the size is equally divided by the number of categories and is rounded down to the nearest integer.\n        val realGeneratedQuestionSize = questionSizePerMode * allCategoryCount\n\n        assertThat(mazeQuizItems).hasSize(realGeneratedQuestionSize)\n    }\n}\n"
  },
  {
    "path": "data/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/di/MathModule.kt",
    "content": "package com.infinitepower.newquiz.data.di\n\nimport com.infinitepower.newquiz.core.math.evaluator.Expressions\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@Module\n@InstallIn(SingletonComponent::class)\nobject MathModule {\n    @Provides\n    @Singleton\n    fun provideExpressions(): Expressions = Expressions()\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/di/RepositoryModule.kt",
    "content": "@file:Suppress(\"unused\")\npackage com.infinitepower.newquiz.data.di\n\nimport com.infinitepower.newquiz.data.repository.UserConfigRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.comparison_quiz.ComparisonQuizApi\nimport com.infinitepower.newquiz.data.repository.comparison_quiz.ComparisonQuizApiImpl\nimport com.infinitepower.newquiz.data.repository.comparison_quiz.ComparisonQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.country.CountryRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.daily_challenge.DailyChallengeRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.home.RecentCategoriesRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.math_quiz.MathQuizCoreRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.maze_quiz.MazeQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.CountryCapitalFlagsQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.FlagQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.GuessMathSolutionRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.LogoQuizRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.MultiChoiceQuestionRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.numbers.NumberTriviaQuestionApiImpl\nimport com.infinitepower.newquiz.data.repository.numbers.NumberTriviaQuestionRepositoryImpl\nimport com.infinitepower.newquiz.data.repository.wordle.WordleRepositoryImpl\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.domain.repository.UserConfigRepository\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.CountryCapitalFlagsQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.FlagQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.GuessMathSolutionRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.LogoQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.MultiChoiceQuestionRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionApi\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionRepository\nimport com.infinitepower.newquiz.domain.repository.wordle.WordleRepository\nimport dagger.Binds\nimport dagger.Module\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\n\n@Module\n@InstallIn(SingletonComponent::class)\nabstract class RepositoryModule {\n    @Binds\n    abstract fun bindMultiChoiceQuestionRepository(openTDBRepository: MultiChoiceQuestionRepositoryImpl): MultiChoiceQuestionRepository\n\n    @Binds\n    abstract fun bindSavedMultiChoiceQuestionsRepository(savedQuestionsRepository: SavedMultiChoiceQuestionsRepositoryImpl): SavedMultiChoiceQuestionsRepository\n\n    @Binds\n    abstract fun bindWordleRepository(wordleRepositoryImpl: WordleRepositoryImpl): WordleRepository\n\n    @Binds\n    abstract fun bindFlagQuizRepository(flagQuizRepositoryImpl: FlagQuizRepositoryImpl): FlagQuizRepository\n\n    @Binds\n    abstract fun bindLogoQuizRepository(logoQuizRepositoryImpl: LogoQuizRepositoryImpl): LogoQuizRepository\n\n    @Binds\n    abstract fun bindMathQuizCoreRepository(mathQuizCoreRepositoryImpl: MathQuizCoreRepositoryImpl): MathQuizCoreRepository\n\n    @Binds\n    abstract fun bindMazeMathQuizRepository(mazeMathQuizRepository: MazeQuizRepositoryImpl): MazeQuizRepository\n\n    @Binds\n    abstract fun bindGuessMathSolutionRepository(guessMathSolutionRepository: GuessMathSolutionRepositoryImpl): GuessMathSolutionRepository\n\n    @Binds\n    abstract fun bindNumberTriviaQuestionApi(numbersTriviaQuestionApiImpl: NumberTriviaQuestionApiImpl): NumberTriviaQuestionApi\n\n    @Binds\n    abstract fun bindNumberTriviaQuestionRepository(numberTriviaQuestionRepositoryImpl: NumberTriviaQuestionRepositoryImpl): NumberTriviaQuestionRepository\n\n    @Binds\n    abstract fun bindCountryCapitalFlagsQuizRepository(countryCapitalFlagsQuizRepository: CountryCapitalFlagsQuizRepositoryImpl): CountryCapitalFlagsQuizRepository\n\n    @Binds\n    abstract fun bindComparisonQuizRepository(comparisonQuizRepository: ComparisonQuizRepositoryImpl): ComparisonQuizRepository\n\n    @Binds\n    abstract fun bindDailyChallengeRepository(dailyChallengeRepository: DailyChallengeRepositoryImpl): DailyChallengeRepository\n\n    @Binds\n    abstract fun bindRecentCategoriesRepository(impl: RecentCategoriesRepositoryImpl): RecentCategoriesRepository\n\n    @Binds\n    abstract fun bindCountryRepository(impl: CountryRepositoryImpl): CountryRepository\n\n    @Binds\n    abstract fun bindComparisonQuizApi(impl: ComparisonQuizApiImpl): ComparisonQuizApi\n\n    @Binds\n    abstract fun bindUserConfigRepository(impl: UserConfigRepositoryImpl): UserConfigRepository\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/local/multi_choice_quiz/category/MultiChoiceQuestionCategories.kt",
    "content": "package com.infinitepower.newquiz.data.local.multi_choice_quiz.category\n\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceCategory\nimport com.infinitepower.newquiz.core.R as CoreR\n\nval multiChoiceQuestionCategories = listOf(\n    MultiChoiceCategory(\n        id = MultiChoiceBaseCategory.Logo.id,\n        name = UiText.StringResource(CoreR.string.logo_quiz),\n        image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Flogo_quiz_illustration.jpg?alt=media&token=cd9e54a2-a5d1-45f1-a285-cc490cc44cad\"\n    ),\n    MultiChoiceCategory(\n        id = MultiChoiceBaseCategory.Flag.id,\n        name = UiText.StringResource(CoreR.string.flag_quiz),\n        image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fflags_illustration.png?alt=media&token=ec6b2820-1d26-4352-9c54-201bd387ae94\",\n        requireInternetConnection = false\n    ),\n    MultiChoiceCategory(\n        id = MultiChoiceBaseCategory.CountryCapitalFlags.id,\n        name = UiText.StringResource(CoreR.string.country_capital_flags),\n        image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fflags_illustration.png?alt=media&token=ec6b2820-1d26-4352-9c54-201bd387ae94\",\n        requireInternetConnection = false\n    ),\n    MultiChoiceCategory(\n        id = MultiChoiceBaseCategory.GuessMathSolution.id,\n        name = UiText.StringResource(CoreR.string.guess_solution),\n        image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fnumber_illustration.jpg?alt=media&token=68faf243-2b0e-4a13-aa9c-223743e263fd\",\n        requireInternetConnection = false\n    ),\n    MultiChoiceCategory(\n        id = MultiChoiceBaseCategory.NumberTrivia.id,\n        name = UiText.StringResource(CoreR.string.number_trivia),\n        image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fnumber_12_in_beach.jpg?alt=media&token=9b888c81-c51c-49ac-a376-0b3bde45db36\"\n    ),\n    MultiChoiceCategory(\n        id = \"9\",\n        name = UiText.StringResource(CoreR.string.general_knowledge),\n        image = CoreR.drawable.general_knowledge\n    ),\n    MultiChoiceCategory(\n        id = \"10\",\n        name = UiText.StringResource(CoreR.string.entertainment_books),\n        image = CoreR.drawable.books\n    ),\n    MultiChoiceCategory(\n        id = \"11\",\n        name = UiText.StringResource(CoreR.string.entertainment_film),\n        image = CoreR.drawable.films\n    ),\n    MultiChoiceCategory(\n        id = \"12\",\n        name = UiText.StringResource(CoreR.string.entertainment_music),\n        image = CoreR.drawable.music\n    ),\n    MultiChoiceCategory(\n        id = \"13\",\n        name = UiText.StringResource(CoreR.string.entertainment_musicals_and_theatres),\n        image = CoreR.drawable.musicals_and_theatres\n    ),\n    MultiChoiceCategory(\n        id = \"14\",\n        name = UiText.StringResource(CoreR.string.entertainment_television),\n        image = CoreR.drawable.entertainment_television\n    ),\n    MultiChoiceCategory(\n        id = \"15\",\n        name = UiText.StringResource(CoreR.string.entertainment_video_games),\n        image = CoreR.drawable.entertainment_video_games\n    ),\n    MultiChoiceCategory(\n        id = \"16\",\n        name = UiText.StringResource(CoreR.string.entertainment_board_games),\n        image = CoreR.drawable.entertainment_board_games\n    ),\n    MultiChoiceCategory(\n        id = \"17\",\n        name = UiText.StringResource(CoreR.string.science_and_nature),\n        image = CoreR.drawable.science_and_nature\n    ),\n    MultiChoiceCategory(\n        id = \"18\",\n        name = UiText.StringResource(CoreR.string.science_computers),\n        image = CoreR.drawable.science_computers\n    ),\n    MultiChoiceCategory(\n        id = \"19\",\n        name = UiText.StringResource(CoreR.string.science_mathematics),\n        image = CoreR.drawable.science_mathematics\n    ),\n    MultiChoiceCategory(\n        id = \"20\",\n        name = UiText.StringResource(CoreR.string.mythology),\n        image = CoreR.drawable.mythology\n    ),\n    MultiChoiceCategory(\n        id = \"21\",\n        name = UiText.StringResource(CoreR.string.sports),\n        image = CoreR.drawable.sports\n    ),\n    MultiChoiceCategory(\n        id = \"22\",\n        name = UiText.StringResource(CoreR.string.geography),\n        image = CoreR.drawable.geography\n    ),\n    MultiChoiceCategory(\n        id = \"23\",\n        name = UiText.StringResource(CoreR.string.history),\n        image = CoreR.drawable.history\n    ),\n    MultiChoiceCategory(\n        id = \"24\",\n        name = UiText.StringResource(CoreR.string.politics),\n        image = CoreR.drawable.politics\n    ),\n    MultiChoiceCategory(\n        id = \"25\",\n        name = UiText.StringResource(CoreR.string.art),\n        image = CoreR.drawable.art\n    ),\n    MultiChoiceCategory(\n        id = \"26\",\n        name = UiText.StringResource(CoreR.string.celebrities),\n        image = CoreR.drawable.celebrities\n    ),\n    MultiChoiceCategory(\n        id = \"27\",\n        name = UiText.StringResource(CoreR.string.animals),\n        image = CoreR.drawable.animals\n    ),\n    MultiChoiceCategory(\n        id = \"28\",\n        name = UiText.StringResource(CoreR.string.vehicles),\n        image = CoreR.drawable.vehicles\n    ),\n    MultiChoiceCategory(\n        id = \"29\",\n        name = UiText.StringResource(CoreR.string.entertainment_comics),\n        image = CoreR.drawable.entertainment_comics\n    ),\n    MultiChoiceCategory(\n        id = \"30\",\n        name = UiText.StringResource(CoreR.string.science_gadgets),\n        image = CoreR.drawable.science_gadgets\n    ),\n    MultiChoiceCategory(\n        id = \"31\",\n        name = UiText.StringResource(CoreR.string.entertainment_japanese_anime_and_manga),\n        image = CoreR.drawable.entertainment_japanese_anime_and_manga\n    ),\n    MultiChoiceCategory(\n        id = \"32\",\n        name = UiText.StringResource(CoreR.string.entertainment_cartoon_and_animations),\n        image = CoreR.drawable.entertainment_cartoon_and_animations\n    )\n)"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/local/wordle/WordleCategories.kt",
    "content": "package com.infinitepower.newquiz.data.local.wordle\n\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.wordle.WordleCategory\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport kotlin.random.Random\nimport com.infinitepower.newquiz.core.R as CoreR\n\nobject WordleCategories  {\n    /**\n     * Returns a random [WordleCategory] from the list of [allCategories].\n     * If [isInternetAvailable] is false, it will only return categories that don't require internet connection.\n     *\n     * @param isInternetAvailable Whether the internet is available or not.\n     * @param random The random instance to use.\n     * @see [WordleCategory]\n     */\n    fun random(\n        isInternetAvailable: Boolean,\n        random: Random = Random\n    ): WordleCategory = if (isInternetAvailable) {\n        allCategories.random(random)\n    } else {\n        allCategories.filter { !it.requireInternetConnection }.random(random)\n    }\n\n    val allCategories = listOf(\n        WordleCategory(\n            name = UiText.StringResource(CoreR.string.guess_the_word),\n            image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fwordle_illustration.jpg?alt=media&token=69019438-4904-4656-8b1c-18678c537d6b\",\n            wordleQuizType = WordleQuizType.TEXT\n        ),\n        WordleCategory(\n            name = UiText.StringResource(CoreR.string.guess_the_number),\n            image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fnumbers_12345_illustration.jpg?alt=media&token=f170e7ca-02a3-4dae-87f0-63b0f1205bc5\",\n            wordleQuizType = WordleQuizType.NUMBER\n        ),\n        WordleCategory(\n            name = UiText.StringResource(CoreR.string.guess_math_formula),\n            image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fnumber_illustration.jpg?alt=media&token=68faf243-2b0e-4a13-aa9c-223743e263fd\",\n            wordleQuizType = WordleQuizType.MATH_FORMULA\n        ),\n        WordleCategory(\n            name = UiText.StringResource(CoreR.string.number_trivia),\n            image = \"https://firebasestorage.googleapis.com/v0/b/newquiz-app.appspot.com/o/Illustrations%2Fnumber_12_in_beach.jpg?alt=media&token=9b888c81-c51c-49ac-a376-0b3bde45db36\",\n            wordleQuizType = WordleQuizType.NUMBER_TRIVIA,\n            requireInternetConnection = true\n        )\n    )\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/UserConfigRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository\n\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.domain.repository.UserConfigRepository\nimport com.infinitepower.newquiz.model.regional_preferences.DistanceUnitType\nimport com.infinitepower.newquiz.model.regional_preferences.RegionalPreferences\nimport com.infinitepower.newquiz.model.regional_preferences.TemperatureUnit\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass UserConfigRepositoryImpl @Inject constructor(\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager,\n) : UserConfigRepository {\n    override suspend fun getRegionalPreferences(): RegionalPreferences {\n        val temperatureUnitStr =\n            settingsDataStoreManager.getPreference(SettingsCommon.TemperatureUnit)\n        val temperatureUnit = if (temperatureUnitStr.isBlank()) {\n            null // use default\n        } else {\n            TemperatureUnit.valueOf(temperatureUnitStr)\n        }\n\n        val distanceUnitTypeStr =\n            settingsDataStoreManager.getPreference(SettingsCommon.DistanceUnitType)\n        val distanceUnitType = if (distanceUnitTypeStr.isBlank()) {\n            null // use default\n        } else {\n            DistanceUnitType.valueOf(distanceUnitTypeStr)\n        }\n\n        return RegionalPreferences(\n            temperatureUnit = temperatureUnit,\n            distanceUnitType = distanceUnitType\n        )\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/comparison_quiz/ComparisonQuizApi.kt",
    "content": "package com.infinitepower.newquiz.data.repository.comparison_quiz\n\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport kotlin.random.Random\n\ninterface ComparisonQuizApi {\n    suspend fun generateQuestions(\n        category: ComparisonQuizCategory,\n        size: Int,\n        random: Random = Random\n    ): List<ComparisonQuizItemEntity>\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/comparison_quiz/ComparisonQuizApiImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.comparison_quiz\n\nimport android.util.Log\nimport com.infinitepower.newquiz.core.common.BaseApiUrls\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport io.ktor.client.HttpClient\nimport io.ktor.client.request.headers\nimport io.ktor.client.request.parameter\nimport io.ktor.client.request.request\nimport io.ktor.client.statement.HttpResponse\nimport io.ktor.client.statement.bodyAsText\nimport io.ktor.http.HttpHeaders\nimport io.ktor.http.HttpMethod\nimport kotlinx.serialization.json.Json\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass ComparisonQuizApiImpl @Inject constructor(\n    private val client: HttpClient,\n    private val countryRepository: CountryRepository\n) : ComparisonQuizApi {\n    companion object {\n        private const val TAG = \"ComparisonQuizApiImpl\"\n\n        private const val COUNTRY_POPULATION_CATEGORY_ID = \"country-population\"\n        private const val COUNTRY_AREA_CATEGORY_ID = \"country-area\"\n\n        internal val supportedLocalCategories = setOf(\n            COUNTRY_POPULATION_CATEGORY_ID,\n            COUNTRY_AREA_CATEGORY_ID\n        )\n    }\n\n    override suspend fun generateQuestions(\n        category: ComparisonQuizCategory,\n        size: Int,\n        random: Random\n    ): List<ComparisonQuizItemEntity> {\n        return if (category.generateQuestionsLocally && category.id in supportedLocalCategories) {\n            generateQuestionsLocally(\n                category = category,\n                size = size,\n                random = random\n            )\n        } else {\n            getQuestionsFromRemoteApi(\n                category = category,\n                size = size\n            )\n        }\n    }\n\n    private suspend fun getQuestionsFromRemoteApi(\n        category: ComparisonQuizCategory,\n        size: Int\n    ): List<ComparisonQuizItemEntity> {\n        Log.d(TAG, \"Getting questions from remote API with category: ${category.id}\")\n\n        val apiUrl = \"${BaseApiUrls.NEWQUIZ}/api/comparisonquiz/${category.id}\"\n\n        val response: HttpResponse = client.request(apiUrl) {\n            headers {\n                append(HttpHeaders.Accept, \"application/json\")\n            }\n            method = HttpMethod.Get\n            parameter(\"size\", size)\n        }\n\n        val textResponse = response.bodyAsText()\n        return Json.decodeFromString(textResponse)\n    }\n\n    private suspend fun generateQuestionsLocally(\n        category: ComparisonQuizCategory,\n        size: Int,\n        random: Random\n    ): List<ComparisonQuizItemEntity> {\n        return when (category.id) {\n            COUNTRY_POPULATION_CATEGORY_ID, COUNTRY_AREA_CATEGORY_ID -> generateCountryQuestions(\n                category = category,\n                size = size,\n                random = random\n            )\n            else -> throw UnsupportedOperationException(\"Category not supported: ${category.id}\")\n        }\n    }\n\n    private suspend fun generateCountryQuestions(\n        category: ComparisonQuizCategory,\n        size: Int,\n        random: Random\n    ): List<ComparisonQuizItemEntity> {\n        Log.d(TAG, \"Generating country questions locally with category: ${category.id}\")\n\n        val countries = countryRepository\n            .getAllCountries()\n            .shuffled(random)\n            .take(size)\n\n        return countries.map { country ->\n            val value = when (category.id) {\n                COUNTRY_POPULATION_CATEGORY_ID -> country.population.toDouble()\n                COUNTRY_AREA_CATEGORY_ID -> country.area\n                else -> throw UnsupportedOperationException(\"Category not supported: ${category.id}\")\n            }\n\n            ComparisonQuizItemEntity(\n                title = country.countryName,\n                value = value,\n                imgUrl = country.flagImage.toASCIIString()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/comparison_quiz/ComparisonQuizRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.comparison_quiz\n\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.data.util.mappers.comparisonquiz.toModel\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategoryEntity\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.serialization.json.Json\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass ComparisonQuizRepositoryImpl @Inject constructor(\n    private val remoteConfig: RemoteConfig,\n    private val gameResultDao: GameResultDao,\n    private val comparisonQuizApi: ComparisonQuizApi\n) : ComparisonQuizRepository {\n    private val categoriesCache: MutableList<ComparisonQuizCategory> = mutableListOf()\n\n    override fun getCategories(): List<ComparisonQuizCategory> {\n        if (categoriesCache.isEmpty()) {\n            val categoriesStr = remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_CATEGORIES)\n            val categoriesEntity: List<ComparisonQuizCategoryEntity> =\n                Json.decodeFromString(categoriesStr)\n            val categories = categoriesEntity.map(ComparisonQuizCategoryEntity::toModel)\n\n            categoriesCache.addAll(categories)\n        }\n\n        return categoriesCache\n    }\n\n    override fun getCategoryById(id: String): ComparisonQuizCategory? {\n        return getCategories().find { it.id == id }\n    }\n\n    override suspend fun getQuestions(\n        category: ComparisonQuizCategory,\n        size: Int,\n        random: Random\n    ): List<ComparisonQuizItem> {\n        val entityQuestions = comparisonQuizApi.generateQuestions(\n            category = category,\n            size = size,\n            random = random\n        )\n\n        return entityQuestions.map(ComparisonQuizItemEntity::toModel)\n    }\n\n    override suspend fun getHighestPosition(categoryId: String): Int {\n        return gameResultDao.getComparisonQuizHighestPosition(categoryId)\n    }\n\n    override fun getHighestPositionFlow(categoryId: String): Flow<Int> {\n        return gameResultDao.getComparisonQuizHighestPositionFlow(categoryId)\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/country/CountryEntity.kt",
    "content": "package com.infinitepower.newquiz.data.repository.country\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.country.Continent\nimport com.infinitepower.newquiz.model.country.Country\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport kotlinx.serialization.Serializable\nimport java.net.URI\n\n@Keep\n@Serializable\ninternal data class CountryEntity(\n    val countryCode: String,\n    val countryName: String,\n    val capital: String,\n    val continent: String,\n    val difficulty: String,\n    val population: Long,\n    val area: Double,\n) : java.io.Serializable\n\ninternal fun CountryEntity.toModel(flagBaseUrl: String): Country {\n    // Format the url like https://flagapi.example/svg/%code%.svg\n    val flagUrl = flagBaseUrl.replace(\"%code%\", countryCode.lowercase())\n\n    return Country(\n        countryCode = countryCode,\n        countryName = countryName,\n        capital = capital,\n        population = population,\n        area = area,\n        continent = Continent.from(continent),\n        difficulty = QuestionDifficulty.from(difficulty),\n        flagImage = URI.create(flagUrl)\n    )\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/country/CountryRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.country\n\nimport android.content.Context\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.util.android.resources.readRawJson\nimport com.infinitepower.newquiz.data.R\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.model.country.Continent\nimport com.infinitepower.newquiz.model.country.Country\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport java.net.URI\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass CountryRepositoryImpl @Inject constructor(\n    @ApplicationContext private val context: Context,\n    private val remoteConfig: RemoteConfig\n) : CountryRepository {\n    companion object {\n        /**\n         * The string that is used to indicate that the flag should be loaded from the local\n         * resources.\n         */\n        private const val LOCAL_FLAG_BASE_URL = \"local\"\n    }\n\n    private fun getCountryFlag(\n        countryCode: String,\n        flagBaseUrl: String\n    ): URI {\n        // Check if the flag loads from the local resource or from the remote server\n        if (flagBaseUrl == LOCAL_FLAG_BASE_URL) {\n            // Get the flag uri from the assets folder\n            return URI.create(\"file:///android_asset/flags/${countryCode.lowercase()}.svg\")\n        }\n\n        // Load the flag from the url\n        val flagUrl = flagBaseUrl.replace(\"%code%\", countryCode.lowercase())\n\n        return URI.create(flagUrl)\n    }\n\n    override suspend fun getAllCountries(): List<Country> {\n        val flagBaseUrl = remoteConfig.get(RemoteConfigValue.FLAG_BASE_URL)\n\n        return getCountryFromJson().map { entity ->\n            val flagImage = getCountryFlag(entity.countryCode, flagBaseUrl)\n\n            Country(\n                countryCode = entity.countryCode,\n                countryName = entity.countryName,\n                capital = entity.capital,\n                population = entity.population,\n                area = entity.area,\n                continent = Continent.from(entity.continent),\n                difficulty = QuestionDifficulty.from(entity.difficulty),\n                flagImage = flagImage\n            )\n        }\n    }\n\n    private suspend fun getCountryFromJson(): List<CountryEntity> {\n        return context\n            .resources\n            .readRawJson<List<CountryEntity>>(R.raw.all_countries)\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/daily_challenge/DailyChallengeRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.daily_challenge\n\nimport com.infinitepower.newquiz.core.database.dao.DailyChallengeDao\nimport com.infinitepower.newquiz.core.database.model.DailyChallengeTaskEntity\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.util.mappers.daily_challenge.toModel\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.map\nimport kotlinx.datetime.Clock\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\nimport kotlin.time.Duration.Companion.days\n\n@Singleton\nclass DailyChallengeRepositoryImpl @Inject constructor(\n    private val dailyChallengeDao: DailyChallengeDao,\n    private val comparisonQuizRepository: ComparisonQuizRepository,\n    private val remoteConfig: RemoteConfig,\n    private val userService: UserService\n) : DailyChallengeRepository {\n    override fun getAvailableTasksFlow(): Flow<List<DailyChallengeTask>> {\n        val comparisonQuizCategories = comparisonQuizRepository.getCategories()\n\n        return dailyChallengeDao\n            .getAllTasksFlow()\n            .map { tasks ->\n                val now = Clock.System.now().toEpochMilliseconds()\n\n                tasks\n                    .filter { task -> task.startDate <= now && task.endDate >= now }\n                    .map { task -> task.toModel(comparisonQuizCategories) }\n            }\n    }\n\n    override suspend fun getAllTasks(): List<DailyChallengeTask> {\n        val comparisonQuizCategories = comparisonQuizRepository.getCategories()\n\n        return dailyChallengeDao\n            .getAllTasks()\n            .map { task -> task.toModel(comparisonQuizCategories) }\n    }\n\n    override suspend fun getAvailableTasks(): List<DailyChallengeTask> {\n        val comparisonQuizCategories = comparisonQuizRepository.getCategories()\n        val now = Clock.System.now().toEpochMilliseconds()\n\n        return dailyChallengeDao\n            .getAllTasks()\n            .filter { task -> now in task.startDate..task.endDate }\n            .map { task -> task.toModel(comparisonQuizCategories) }\n    }\n\n    override fun getClaimableTasksCountFlow(): Flow<Int> = getAvailableTasksFlow().map { tasks ->\n        tasks.count { task -> task.isClaimable() }\n    }\n\n    override suspend fun checkAndGenerateTasksIfNeeded(\n        tasksToGenerate: Int,\n        random: Random\n    ) {\n        val now = Clock.System.now().toEpochMilliseconds()\n        val tasksAreExpired = !dailyChallengeDao.tasksAreAvailable(now)\n\n        // Check if the daily tasks are expired\n        if (tasksAreExpired) {\n            generateDailyTasks(\n                tasksToGenerate = tasksToGenerate,\n                random = random\n            )\n        }\n    }\n\n    private suspend fun generateDailyTasks(\n        tasksToGenerate: Int,\n        random: Random = Random\n    ) {\n        val now = Clock.System.now()\n        val dateRange = now.rangeTo(now + 1.days)\n\n        val types = GameEvent.getRandomEvents(\n            count = tasksToGenerate,\n            multiChoiceCategories = multiChoiceQuestionCategories,\n            comparisonQuizCategories = comparisonQuizRepository.getCategories(),\n            random = random\n        )\n\n        val diamondsReward =\n            remoteConfig.get(RemoteConfigValue.DAILY_CHALLENGE_ITEM_REWARD).toUInt()\n\n        val newTasks = types.map { type ->\n            val maxValue = type.valueRange.toList().random(random)\n\n            DailyChallengeTaskEntity(\n                id = random.nextInt(),\n                diamondsReward = diamondsReward.toInt(),\n                experienceReward = (10..100).random(random),\n                isClaimed = false,\n                currentValue = 0,\n                maxValue = maxValue.toInt(),\n                type = type.key,\n                startDate = dateRange.start.toEpochMilliseconds(),\n                endDate = dateRange.endInclusive.toEpochMilliseconds()\n            )\n        }\n\n        dailyChallengeDao.insertAll(newTasks)\n    }\n\n    override suspend fun completeTaskStep(taskType: GameEvent) {\n        val task = dailyChallengeDao.getTaskByType(taskType.key)\n            ?: throw NullPointerException(\"Task not found.\")\n\n        val now = Clock.System.now()\n\n        // Check if the task is expired\n        val taskIsAvailable = now.toEpochMilliseconds() in task.startDate..task.endDate\n        check(taskIsAvailable) { \"Task (${task.id}) is expired.\" }\n\n        // Check if the task is already claimed\n        check(!task.isClaimed) { \"Task (${task.id}) is already claimed.\" }\n\n        // Update the task\n        val newTask = task.copy(currentValue = task.currentValue + 1)\n\n        // Update the tasks set\n        dailyChallengeDao.update(newTask)\n    }\n\n    override suspend fun claimTask(taskType: GameEvent) {\n        val task = dailyChallengeDao.getTaskByType(taskType.key)\n            ?: throw NullPointerException(\"Task not found.\")\n\n        val now = Clock.System.now()\n\n        // Check if the task is expired\n        val taskIsAvailable = now.toEpochMilliseconds() in task.startDate..task.endDate\n        check(taskIsAvailable) { \"Task (${task.id}) is expired.\" }\n\n        // Check if the task is already claimed\n        check(!task.isClaimed) { \"Task (${task.id}) is already claimed.\" }\n\n        check(task.currentValue >= task.maxValue) {\n            \"Task (${task.id}) is not completed.\"\n        }\n\n        // Update the task\n        val newTask = task.copy(isClaimed = true)\n\n        // Update the tasks set\n        dailyChallengeDao.update(newTask)\n\n        // Give the user the reward\n        userService.addRemoveDiamonds(newTask.diamondsReward)\n    }\n\n    override suspend fun resetTasks() {\n        dailyChallengeDao.deleteAll()\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/daily_challenge/util/DailyChallengeTypeTitleUtil.kt",
    "content": "package com.infinitepower.newquiz.data.repository.daily_challenge.util\n\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.util.translation.getWordleTitle\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport com.infinitepower.newquiz.core.R as CoreR\n\nfun GameEvent.getTitle(\n    maxValue: Int,\n    comparisonQuizCategories: List<ComparisonQuizCategory>\n): UiText {\n    return when (this) {\n        // Multi choice quiz\n        GameEvent.MultiChoice.PlayRandomQuiz -> UiText.PluralStringResource(\n            resId = CoreR.plurals.play_random_multi_choice_quiz_game,\n            quantity = maxValue,\n            maxValue\n        )\n        GameEvent.MultiChoice.EndQuiz -> UiText.PluralStringResource(\n            resId = CoreR.plurals.end_multi_choice_quiz_game,\n            quantity = maxValue,\n            maxValue\n        )\n        GameEvent.MultiChoice.PlayQuestions -> UiText.PluralStringResource(\n            resId = CoreR.plurals.play_multi_choice_quiz_questions,\n            quantity = maxValue,\n            maxValue\n        )\n        GameEvent.MultiChoice.GetAnswersCorrect -> UiText.PluralStringResource(\n            resId = CoreR.plurals.get_multi_choice_quiz_answer_correct,\n            quantity = maxValue,\n            maxValue\n        )\n        is GameEvent.MultiChoice.PlayQuizWithCategory -> {\n            val category = multiChoiceQuestionCategories.first { it.id == this.categoryId }\n\n            UiText.PluralStringResource(\n                resId = CoreR.plurals.play_multi_choice_quiz_game_in_category,\n                quantity = maxValue,\n                maxValue,\n                category.name\n            )\n        }\n\n        // Wordle\n        GameEvent.Wordle.GetWordCorrect -> UiText.PluralStringResource(\n            resId = CoreR.plurals.get_wordle_word_correct,\n            quantity = maxValue,\n            maxValue\n        )\n        is GameEvent.Wordle.PlayWordWithCategory -> {\n            val categoryName = wordleCategory.getWordleTitle()\n\n            UiText.PluralStringResource(\n                resId = CoreR.plurals.play_wordle_game_in_category,\n                quantity = maxValue,\n                maxValue,\n                categoryName\n            )\n        }\n\n        // Comparison quiz\n        is GameEvent.ComparisonQuiz.PlayQuizWithCategory -> {\n            val categoryName = comparisonQuizCategories\n                .find { it.id == this.categoryId }\n                ?.name ?: \"\"\n\n            UiText.PluralStringResource(\n                resId = CoreR.plurals.play_comparison_quiz_game_in_category,\n                quantity = maxValue,\n                maxValue,\n                categoryName\n            )\n        }\n        is GameEvent.ComparisonQuiz.PlayAndGetScore -> UiText.PluralStringResource(\n            resId = CoreR.plurals.play_comparison_quiz_game_and_get_score,\n            quantity = maxValue,\n            maxValue,\n            score\n        )\n        is GameEvent.ComparisonQuiz.PlayWithComparisonMode -> {\n            val modeName = if (mode == ComparisonMode.GREATER) {\n                UiText.StringResource(CoreR.string.greater)\n            } else {\n                UiText.StringResource(CoreR.string.lesser)\n            }\n\n            UiText.PluralStringResource(\n                resId = CoreR.plurals.play_comparison_quiz_game_in_comparison_mode,\n                quantity = maxValue,\n                maxValue,\n                modeName\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/home/RecentCategoriesRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.home\n\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport com.infinitepower.newquiz.core.datastore.common.RecentCategoryDataStoreCommon\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.di.RecentCategoriesDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.local.wordle.WordleCategories\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.home.HomeCategories\nimport com.infinitepower.newquiz.domain.repository.home.HomeCategoriesFlow\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceCategory\nimport com.infinitepower.newquiz.model.wordle.WordleCategory\nimport kotlinx.collections.immutable.toImmutableList\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.map\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass RecentCategoriesRepositoryImpl @Inject constructor(\n    @RecentCategoriesDataStoreManager private val recentCategoriesDataStoreManager: DataStoreManager,\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager,\n    private val comparisonQuizRepository: ComparisonQuizRepository,\n    private val remoteConfig: RemoteConfig\n) : RecentCategoriesRepository {\n    override fun getMultiChoiceCategories(\n        isInternetAvailable: Boolean\n    ): HomeCategoriesFlow<MultiChoiceCategory> = getHomeCategories(\n        allCategories = multiChoiceQuestionCategories,\n        request = RecentCategoryDataStoreCommon.MultiChoice,\n        isInternetAvailable = isInternetAvailable\n    )\n\n    override fun getWordleCategories(\n        isInternetAvailable: Boolean\n    ): HomeCategoriesFlow<WordleCategory> = getHomeCategories(\n        allCategories = WordleCategories.allCategories,\n        request = RecentCategoryDataStoreCommon.Wordle,\n        isInternetAvailable = isInternetAvailable\n    )\n\n    override fun getComparisonCategories(\n        isInternetAvailable: Boolean\n    ): HomeCategoriesFlow<ComparisonQuizCategory> = getHomeCategories(\n        allCategories = comparisonQuizRepository.getCategories(),\n        request = RecentCategoryDataStoreCommon.ComparisonQuiz,\n        isInternetAvailable = isInternetAvailable\n    )\n\n    private fun <T : BaseCategory> getHomeCategories(\n        allCategories: List<T>,\n        request: PreferenceRequest<Set<String>>,\n        isInternetAvailable: Boolean\n    ): Flow<HomeCategories<T>> = getHomeCategories(\n        allCategories = allCategories,\n        recentCategoriesFlow = recentCategoriesDataStoreManager.getPreferenceFlow(request),\n        hideOnlineCategoriesFlow = settingsDataStoreManager.getPreferenceFlow(SettingsCommon.HideOnlineCategories),\n        isInternetAvailable = isInternetAvailable\n    )\n\n    internal fun <T : BaseCategory> getHomeCategories(\n        allCategories: List<T>,\n        recentCategoriesFlow: Flow<Set<String>>,\n        hideOnlineCategoriesFlow: Flow<Boolean>,\n        isInternetAvailable: Boolean\n    ) = combine(\n        recentCategoriesFlow,\n        hideOnlineCategoriesFlow\n    ) { recentCategoriesIds, hideOnlineCategories ->\n        val shouldHideCategories = hideOnlineCategories && !isInternetAvailable\n\n        val allCategoriesFiltered = if (shouldHideCategories) {\n            allCategories.filter { !it.requireInternetConnection }\n        } else {\n            allCategories\n        }\n\n        getHomeBaseCategories(\n            savedRecentCategoriesIds = recentCategoriesIds,\n            allCategories = allCategoriesFiltered,\n            isInternetAvailable = isInternetAvailable\n        )\n    }\n\n    private fun <T : BaseCategory> getHomeBaseCategories(\n        savedRecentCategoriesIds: Set<String>,\n        allCategories: List<T>,\n        isInternetAvailable: Boolean\n    ): HomeCategories<T> {\n        val savedRecentCategories = savedRecentCategoriesIds.mapNotNull { id ->\n            allCategories.find { it.id == id }\n        }\n\n        val recentCategories = getRecentCategories(\n            recentCategories = savedRecentCategories,\n            allCategories = allCategories,\n            isInternetAvailable = isInternetAvailable\n        )\n\n        val otherCategories = allCategories - recentCategories.toSet()\n\n        return HomeCategories(\n            recentCategories = recentCategories.toImmutableList(),\n            otherCategories = otherCategories.sortByInternetConnection(isInternetAvailable).toImmutableList()\n        )\n    }\n\n    /**\n     * If there is internet available, we return all the categories normally,\n     * but if there is no internet, we make the categories that don't require internet connection\n     * in the top of the list.\n     */\n    private fun <T : BaseCategory> List<T>.sortByInternetConnection(\n        isInternetAvailable: Boolean\n    ): List<T> = if (isInternetAvailable) this else sortedBy { it.requireInternetConnection }\n\n    private fun <T : BaseCategory> getRecentCategories(\n        recentCategories: List<T>,\n        allCategories: List<T>,\n        isInternetAvailable: Boolean\n    ): List<T> {\n        // When there are recent categories, we return them\n        return recentCategories.ifEmpty {\n            // If there are no recent categories, we take 3 random ones,\n            // So we don't show all categories initially\n            allCategories\n                // If there is no internet, we only show the categories that don't require internet connection\n                .filter { !it.requireInternetConnection || isInternetAvailable }\n                // If there are no categories that don't require internet connection, we use all categories\n                .ifEmpty { allCategories }\n                .shuffled()\n                .take(3)\n        }\n    }\n\n    /**\n     * Get the default value for the [ShowCategoryConnectionInfo] settings preference\n     * from the remote config.\n     */\n    override fun getDefaultShowCategoryConnectionInfo(): ShowCategoryConnectionInfo {\n        return remoteConfig.get(RemoteConfigValue.DEFAULT_SHOW_CATEGORY_CONNECTION_INFO)\n    }\n\n    override fun getShowCategoryConnectionInfoFlow(): Flow<ShowCategoryConnectionInfo> {\n        val default = getDefaultShowCategoryConnectionInfo()\n\n        return settingsDataStoreManager\n            .getPreferenceFlow(SettingsCommon.CategoryConnectionInfoBadge(default))\n            .map(ShowCategoryConnectionInfo::valueOf)    }\n\n    override suspend fun addMultiChoiceCategory(categoryId: String) {\n        addCategory(categoryId, RecentCategoryDataStoreCommon.MultiChoice)\n    }\n\n    override suspend fun addWordleCategory(categoryId: String) {\n        addCategory(categoryId, RecentCategoryDataStoreCommon.Wordle)\n    }\n\n    override suspend fun addComparisonCategory(categoryId: String) {\n        addCategory(categoryId, RecentCategoryDataStoreCommon.ComparisonQuiz)\n    }\n\n    private suspend fun addCategory(\n        id: String,\n        preferenceRequest: PreferenceRequest<Set<String>>\n    ) {\n        val recentCategories = recentCategoriesDataStoreManager.getPreference(preferenceRequest)\n\n        val newCategoriesIds = recentCategories\n            .toMutableSet()\n            .apply {\n                // If the category to add is in the recent it's not necessary\n                // to add the category, so return\n                if (id in this) return\n\n                if (size >= 3) remove(last())\n\n                add(id)\n            }.toSet()\n\n        recentCategoriesDataStoreManager.editPreference(\n            key = preferenceRequest.key,\n            newValue = newCategoriesIds\n        )\n    }\n\n    override suspend fun cleanAllSavedCategories() {\n        recentCategoriesDataStoreManager.editPreference(\n            key = RecentCategoryDataStoreCommon.MultiChoice.key,\n            newValue = emptySet()\n        )\n        recentCategoriesDataStoreManager.editPreference(\n            key = RecentCategoryDataStoreCommon.Wordle.key,\n            newValue = emptySet()\n        )\n        recentCategoriesDataStoreManager.editPreference(\n            key = RecentCategoryDataStoreCommon.ComparisonQuiz.key,\n            newValue = emptySet()\n        )\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/math_quiz/MathQuizCoreRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.math_quiz\n\nimport android.os.Build\nimport com.infinitepower.newquiz.core.math.evaluator.Expressions\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository.Companion.numbers\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository.Companion.operators\nimport com.infinitepower.newquiz.model.math_quiz.MathFormula\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass MathQuizCoreRepositoryImpl @Inject constructor(\n    private val expressions: Expressions\n) : MathQuizCoreRepository {\n    private val allNumbers by lazy { 0..9 }\n    private val allOperators by lazy { \"+-*/=\" }\n\n    override fun generateMathFormula(\n        operatorSize: Int,\n        difficulty: QuestionDifficulty,\n        random: Random\n    ): MathFormula {\n        val formula = StringBuilder()\n        var operatorCount = 0\n\n        // Add the first number\n        formula.append(getRandomNumber(difficulty, random))\n\n        while (operatorCount < operatorSize && formula.length < MathQuizCoreRepository.MAX_FORMULA_LENGTH) {\n            // Get the operator\n            val operator = getRandomOperator(difficulty, random)\n\n            // Get the number\n            val number = getRandomNumber(difficulty, random)\n\n            if (operator == '/' && number == 0) {\n                // Skip division by zero\n                continue\n            }\n\n            val newFormula = StringBuilder(formula).apply {\n                append(operator)\n                append(number)\n            }\n\n            // Check if the formula is in range\n            if (getSolution(newFormula.toString()) !in MathQuizCoreRepository.SOLUTION_RANGE) {\n                // Skip if the formula is not in range\n\n                continue\n            }\n\n            formula.append(operator)\n            formula.append(number)\n\n            operatorCount++\n        }\n\n        return MathFormula(formula.toString(), getSolution(formula.toString()))\n    }\n\n    private fun getRandomNumber(\n        difficulty: QuestionDifficulty,\n        random: Random = Random\n    ): Int = difficulty.numbers.random(random)\n\n    private fun getRandomOperator(\n        difficulty: QuestionDifficulty,\n        random: Random = Random\n    ): Char = difficulty.operators.random(random)\n\n    private fun getSolution(formula: String): Int {\n        val solutionBigInt = expressions.eval(formula).toBigInteger()\n\n        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n            solutionBigInt.intValueExact()\n        } else {\n            solutionBigInt.toInt()\n        }\n    }\n\n    override fun validateFormula(formula: String): Boolean {\n        val allNumbersStr = allNumbers.map(Int::digitToChar)\n        val allItemsNumberOrOperator = formula.all {\n            it in allNumbersStr || it in allOperators\n        }\n\n        if (!allItemsNumberOrOperator) return false\n\n        // Checks if formula contains one equals\n        if (formula.count { it == '=' } != 1) return false\n\n        val leftExpression = formula.takeWhile { it != '=' }\n        val rightSolution = formula\n            .takeLastWhile { it != '=' }\n            .toDoubleOrNull() ?: return false\n\n        // Evaluate left-hand side expression and compare it with right-hand side solution\n        return try {\n            val solution = expressions.eval(leftExpression).toDouble()\n            solution == rightSolution\n        } catch (e: Exception) {\n            false\n        }\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/maze_quiz/MazeQuizRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.maze_quiz\n\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.database.dao.MazeQuizDao\nimport com.infinitepower.newquiz.core.database.model.MazeQuizItemEntity\nimport com.infinitepower.newquiz.data.util.mappers.maze.toEntity\nimport com.infinitepower.newquiz.data.util.mappers.maze.toMazeQuizItem\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport kotlinx.collections.immutable.toPersistentList\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.map\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass MazeQuizRepositoryImpl @Inject constructor(\n    private val mazeQuizDao: MazeQuizDao,\n    private val analyticsHelper: AnalyticsHelper\n) : MazeQuizRepository {\n    override fun getSavedMazeQuizFlow(): Flow<MazeQuiz> = mazeQuizDao\n        .getAllMazeItemsFlow()\n        .map { entities -> entities.map(MazeQuizItemEntity::toMazeQuizItem) }\n        .map { mazeItems -> MazeQuiz(items = mazeItems.toPersistentList()) }\n\n    override suspend fun countAllItems(): Int = mazeQuizDao.countAllItems()\n\n    override suspend fun insertItems(items: List<MazeQuiz.MazeItem>) {\n        val entities = items.map(MazeQuiz.MazeItem::toEntity)\n        mazeQuizDao.insertItems(entities)\n    }\n\n    override suspend fun removeItems(items: List<MazeQuiz.MazeItem>) {\n        val entities = items.map(MazeQuiz.MazeItem::toEntity)\n        mazeQuizDao.removeItems(entities)\n    }\n\n    override suspend fun getMazeItemById(id: Int): MazeQuiz.MazeItem? {\n        return mazeQuizDao.getMazeItemById(id)?.toMazeQuizItem()\n    }\n\n    override suspend fun getNextAvailableMazeItem(): MazeQuiz.MazeItem? {\n        return mazeQuizDao.getFirstAvailableMazeItem()?.toMazeQuizItem()\n    }\n\n    override suspend fun completeMazeItem(id: Int) {\n        val allMazeItems = mazeQuizDao.getAllMazeItems()\n\n        val mazeItem = allMazeItems.find { item ->\n            item.id == id\n        } ?: throw NullPointerException(\"Maze item with id $id not found\")\n\n        val updatedMazeItem = mazeItem.copy(played = true)\n\n        mazeQuizDao.updateItem(updatedMazeItem)\n\n        // Checks if is maze completed\n        val isMazeCompleted = allMazeItems.all { it.played }\n        if (isMazeCompleted) analyticsHelper.logEvent(AnalyticsEvent.MazeCompleted(allMazeItems.size))\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/CountryCapitalFlagsQuizRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.CountryCapitalFlagsQuizRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.model.country.Country\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass CountryCapitalFlagsQuizRepositoryImpl @Inject constructor(\n    private val countryRepository: CountryRepository\n) : CountryCapitalFlagsQuizRepository {\n    override suspend fun getRandomQuestions(\n        amount: Int,\n        category: MultiChoiceBaseCategory.CountryCapitalFlags,\n        difficulty: String?,\n        random: Random\n    ): List<MultiChoiceQuestion> {\n        val questionDifficulty = difficulty?.let { QuestionDifficulty.from(it) }\n\n        val allCountries = countryRepository.getAllCountries()\n        val allCountriesCapitalNames = allCountries.map(Country::capital)\n\n        // Filter countries by difficulty\n        val countriesFiltered = questionDifficulty?.let {\n            allCountries.filter { country -> country.difficulty == it }\n        } ?: allCountries // if difficulty is null, then return all countries\n\n        return countriesFiltered\n            .sortedBy { it.countryName }\n            .shuffled(random)\n            .take(amount)\n            .map { country ->\n                country.toQuestion(\n                    allCountriesCapitalNames = allCountriesCapitalNames,\n                    random = random\n                )\n            }\n    }\n\n    private fun Country.toQuestion(\n        allCountriesCapitalNames: List<String>,\n        random: Random = Random\n    ): MultiChoiceQuestion {\n        val answerCountries = allCountriesCapitalNames.shuffled(random).take(3) + capital\n        val answers = answerCountries.shuffled(random)\n\n        return MultiChoiceQuestion(\n            description = \"What is the capital of $countryName?\",\n            image = flagImage,\n            answers = answers,\n            correctAns = answers.indexOf(capital),\n            category = MultiChoiceBaseCategory.CountryCapitalFlags,\n            difficulty = difficulty,\n            lang = QuestionLanguage.EN,\n            type = MultiChoiceQuestionType.MULTIPLE\n        )\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/FlagQuizRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.FlagQuizRepository\nimport com.infinitepower.newquiz.model.country.Country\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass FlagQuizRepositoryImpl @Inject constructor(\n    private val countryRepository: CountryRepository\n) : FlagQuizRepository {\n    override suspend fun getRandomQuestions(\n        amount: Int,\n        category: MultiChoiceBaseCategory.Flag,\n        difficulty: String?,\n        random: Random\n    ): List<MultiChoiceQuestion> {\n        val questionDifficulty = difficulty?.let { QuestionDifficulty.from(it) }\n\n        val allCountries = countryRepository.getAllCountries()\n        val allCountriesNames = allCountries.map(Country::countryName)\n\n        // Filter countries by difficulty\n        val countriesFiltered = questionDifficulty?.let {\n            allCountries.filter { country -> country.difficulty == it }\n        } ?: allCountries // if difficulty is null, then return all countries\n\n\n        return countriesFiltered\n            .sortedBy { it.countryName }\n            .shuffled(random)\n            .take(amount)\n            .map { country ->\n                country.toQuestion(\n                    allCountriesNames = allCountriesNames,\n                    random = random\n                )\n            }\n    }\n\n    private fun Country.toQuestion(\n        allCountriesNames: List<String>,\n        random: Random = Random\n    ): MultiChoiceQuestion {\n        val answerCountriesNames = allCountriesNames.shuffled(random).take(3) + countryName\n        val answers = answerCountriesNames.shuffled(random)\n\n        return MultiChoiceQuestion(\n            description = \"What is the country of this flag?\",\n            image = flagImage,\n            answers = answers,\n            correctAns = answers.indexOf(countryName),\n            category = MultiChoiceBaseCategory.Flag,\n            difficulty = difficulty,\n            lang = QuestionLanguage.EN,\n            type = MultiChoiceQuestionType.MULTIPLE\n        )\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/GuessMathSolutionRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.core.util.kotlin.generateIncorrectNumberAnswers\nimport com.infinitepower.newquiz.core.util.kotlin.generateRandomUniqueItems\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository.Companion.operatorSizeRange\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.GuessMathSolutionRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass GuessMathSolutionRepositoryImpl @Inject constructor(\n    private val mathQuizCoreRepository: MathQuizCoreRepository\n) : GuessMathSolutionRepository {\n    override suspend fun getRandomQuestions(\n        amount: Int,\n        category: MultiChoiceBaseCategory.GuessMathSolution,\n        difficulty: String?,\n        random: Random\n    ): List<MultiChoiceQuestion> {\n        val questionDifficulty = if (difficulty == null) {\n            QuestionDifficulty.random(random)\n        } else {\n            QuestionDifficulty.from(difficulty)\n        }\n\n        return generateRandomUniqueItems(\n            itemCount = amount,\n            generator = {\n                mathQuizCoreRepository.generateMathFormula(\n                    difficulty = questionDifficulty,\n                    operatorSize = questionDifficulty.operatorSizeRange.random(random),\n                    random = random\n                )\n            }\n        ).map { formula ->\n            val incorrectAnswers = generateIncorrectNumberAnswers(3, formula.solution)\n            val answers = incorrectAnswers + formula.solution\n            val answersShuffled = answers.shuffled(random)\n\n            MultiChoiceQuestion(\n                description = \"What is the solution of ${formula.leftFormula} ?\",\n                answers = answersShuffled.map(Int::toString),\n                category = MultiChoiceBaseCategory.GuessMathSolution,\n                correctAns = answersShuffled.indexOf(formula.solution),\n                difficulty = questionDifficulty,\n                lang = QuestionLanguage.EN,\n                type = MultiChoiceQuestionType.MULTIPLE\n            )\n        }\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/LogoQuizRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.LogoQuizRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.model.multi_choice_quiz.logo_quiz.LogoQuizBaseItem\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport kotlinx.serialization.json.Json\nimport java.net.URI\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass LogoQuizRepositoryImpl @Inject constructor(\n    private val remoteConfig: RemoteConfig\n) : LogoQuizRepository {\n    override suspend fun getRandomQuestions(\n        amount: Int,\n        category: MultiChoiceBaseCategory.Logo,\n        difficulty: String?,\n        random: Random\n    ): List<MultiChoiceQuestion> {\n        val allLogos = getRemoteConfigAllLogos()\n\n        val filteredByDifficulty = if (difficulty != null) {\n            allLogos.filter { item -> item.difficulty == difficulty }\n        } else allLogos\n\n        return filteredByDifficulty\n            .sortedBy { it.name }\n            .shuffled(random)\n            .take(amount)\n            .map { item -> item.toQuestion() }\n    }\n\n    private fun getRemoteConfigAllLogos(): List<LogoQuizBaseItem> {\n        val allLogosQuizStr = remoteConfig.get(RemoteConfigValue.ALL_LOGOS_QUIZ)\n        return Json.decodeFromString(allLogosQuizStr)\n    }\n\n    private fun LogoQuizBaseItem.toQuestion(\n        random: Random = Random\n    ): MultiChoiceQuestion {\n\n        val answerCountries = incorrectAnswers.shuffled(random) + name\n        val answers = answerCountries.shuffled(random)\n\n        return MultiChoiceQuestion(\n            description = description,\n            image = URI.create(imgUrl),\n            answers = answers,\n            correctAns = answers.indexOf(name),\n            category = MultiChoiceBaseCategory.Logo,\n            difficulty = QuestionDifficulty.Medium,\n            lang = QuestionLanguage.EN,\n            type = MultiChoiceQuestionType.MULTIPLE\n        )\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/MultiChoiceQuestionRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.data.repository.multi_choice_quiz.dto.OpenTDBQuestionResponse\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.MultiChoiceQuestionRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport io.ktor.client.HttpClient\nimport io.ktor.client.request.headers\nimport io.ktor.client.request.parameter\nimport io.ktor.client.request.request\nimport io.ktor.client.statement.HttpResponse\nimport io.ktor.client.statement.bodyAsText\nimport io.ktor.http.HttpHeaders\nimport io.ktor.http.HttpMethod\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.async\nimport kotlinx.coroutines.awaitAll\nimport kotlinx.coroutines.withContext\nimport kotlinx.serialization.json.Json\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\nprivate const val OPENTDB_API_URL = \"https://opentdb.com/api.php\"\n\n@Singleton\nclass MultiChoiceQuestionRepositoryImpl @Inject constructor(\n    private val client: HttpClient\n) : MultiChoiceQuestionRepository {\n    override suspend fun getRandomQuestions(\n        amount: Int,\n        category: MultiChoiceBaseCategory.Normal,\n        difficulty: String?,\n        random: Random\n    ): List<MultiChoiceQuestion> = withContext(Dispatchers.IO) {\n        val openTDBResults = getOpenTDBResponse(amount, category, difficulty).results\n\n        val questions = openTDBResults.map { result ->\n            async(Dispatchers.IO) {\n                result.decodeResultToQuestion()\n            }\n        }\n\n        questions.awaitAll()\n    }\n\n    private suspend fun getOpenTDBResponse(\n        amount: Int,\n        category: MultiChoiceBaseCategory.Normal,\n        difficulty: String?\n    ): OpenTDBQuestionResponse {\n        val response: HttpResponse = client.request(OPENTDB_API_URL) {\n            method = HttpMethod.Get\n            parameter(\"encode\", \"base64\")\n            parameter(\"amount\", amount)\n            if (category.hasCategory) {\n                parameter(\"category\", category.categoryId)\n            }\n            if (difficulty != null) parameter(\"difficulty\", difficulty)\n\n            headers {\n                append(HttpHeaders.Accept, \"application/json\")\n            }\n        }\n        val textResponse = response.bodyAsText()\n        return Json.decodeFromString(textResponse)\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/dto/OpenTDBQuestionResponse.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz.dto\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.util.base64.base64Decoded\nimport kotlinx.serialization.Serializable\n\n@Keep\n@Serializable\ndata class OpenTDBQuestionResponse(\n    val response_code: Int,\n    val results: List<OpenTDBResult>\n) : java.io.Serializable {\n    @Keep\n    @Serializable\n    data class OpenTDBResult(\n        val category: String,\n        val type: String,\n        val difficulty: String,\n        val question: String,\n        val correct_answer: String,\n        val incorrect_answers: List<String>\n    ) : java.io.Serializable {\n        private fun decodeBase64OpenTDBResult(): OpenTDBResult = copy(\n            category = category.base64Decoded,\n            type = type.base64Decoded,\n            difficulty = difficulty.base64Decoded,\n            question = question.base64Decoded,\n            correct_answer = correct_answer.base64Decoded,\n            incorrect_answers = incorrect_answers.map { answer -> answer.base64Decoded }\n        )\n\n        private fun toQuestion(): MultiChoiceQuestion {\n            val answers = incorrect_answers.plus(correct_answer).shuffled()\n            val correctAnswerIndex = answers.indexOf(correct_answer)\n\n            return MultiChoiceQuestion(\n                description = question,\n                answers = answers,\n                category = MultiChoiceBaseCategory.fromId(category),\n                correctAns = correctAnswerIndex,\n                type = MultiChoiceQuestionType.valueOf(type.uppercase()),\n                difficulty = QuestionDifficulty.from(difficulty),\n                lang = QuestionLanguage.EN\n            )\n        }\n\n        fun decodeResultToQuestion(): MultiChoiceQuestion = decodeBase64OpenTDBResult().toQuestion()\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/saved_questions/SavedMultiChoiceQuestionsRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz.saved_questions\n\nimport com.infinitepower.newquiz.core.database.dao.SavedMultiChoiceQuestionsDao\nimport com.infinitepower.newquiz.core.database.model.MultiChoiceQuestionEntity\nimport com.infinitepower.newquiz.core.database.util.mappers.toEntity\nimport com.infinitepower.newquiz.core.database.util.mappers.toModel\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.saved.SortSavedQuestionsBy\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.map\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\n@Singleton\nclass SavedMultiChoiceQuestionsRepositoryImpl @Inject constructor(\n    private val savedQuestionsDao: SavedMultiChoiceQuestionsDao\n) : SavedMultiChoiceQuestionsRepository {\n    override suspend fun insertQuestions(questions: List<MultiChoiceQuestion>) {\n        val questionsEntity = questions.map(MultiChoiceQuestion::toEntity)\n        savedQuestionsDao.insertQuestions(questionsEntity)\n    }\n\n    override suspend fun insertQuestions(vararg questions: MultiChoiceQuestion) {\n        val questionsEntity = questions.map(MultiChoiceQuestion::toEntity)\n        savedQuestionsDao.insertQuestions(questionsEntity)\n    }\n\n    override fun getFlowQuestions(\n        sortBy: SortSavedQuestionsBy\n    ): Flow<List<MultiChoiceQuestion>> {\n        val questionsFlow = when (sortBy) {\n            SortSavedQuestionsBy.BY_DEFAULT -> savedQuestionsDao.getFlowQuestions()\n            SortSavedQuestionsBy.BY_DESCRIPTION -> savedQuestionsDao.getFlowQuestionsSortedByDescription()\n            SortSavedQuestionsBy.BY_CATEGORY -> savedQuestionsDao.getFlowQuestionsSortedByCategory()\n        }\n\n        return questionsFlow.map { flowQuestions -> flowQuestions.map(MultiChoiceQuestionEntity::toModel) }\n    }\n\n    override suspend fun getQuestions(): List<MultiChoiceQuestion> = savedQuestionsDao\n        .getQuestions()\n        .map(MultiChoiceQuestionEntity::toModel)\n\n    override fun getCount(): Flow<Int> = savedQuestionsDao.getCount()\n\n    override suspend fun deleteAllSelected(questions: List<MultiChoiceQuestion>) {\n        val questionsEntity = questions.map(MultiChoiceQuestion::toEntity)\n        savedQuestionsDao.deleteAll(questionsEntity)\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/numbers/NumberTriviaQuestionApiImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.numbers\n\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionApi\nimport com.infinitepower.newquiz.model.number.NumberTriviaQuestionsEntity\nimport io.ktor.client.HttpClient\nimport io.ktor.client.request.headers\nimport io.ktor.client.request.parameter\nimport io.ktor.client.request.request\nimport io.ktor.client.statement.HttpResponse\nimport io.ktor.client.statement.bodyAsText\nimport io.ktor.http.HttpHeaders\nimport kotlinx.serialization.decodeFromString\nimport kotlinx.serialization.json.Json\nimport javax.inject.Inject\nimport javax.inject.Singleton\n\nprivate const val NUMBERS_API_URL = \"https://newquiz-app.vercel.app/api/numberTrivia/random\"\n\n@Singleton\nclass NumberTriviaQuestionApiImpl @Inject constructor(\n    private val client: HttpClient\n) : NumberTriviaQuestionApi {\n    override suspend fun getRandomQuestion(\n        size: Int,\n        minNumber: Int,\n        maxNumber: Int\n    ): NumberTriviaQuestionsEntity {\n        val response: HttpResponse = client.request(NUMBERS_API_URL) {\n            parameter(\"size\", size)\n            parameter(\"min\", minNumber)\n            parameter(\"max\", maxNumber)\n\n            headers {\n                append(HttpHeaders.Accept, \"application/json\")\n            }\n        }\n\n        val textResponse = response.bodyAsText()\n        return Json.decodeFromString(textResponse)\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/numbers/NumberTriviaQuestionRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.numbers\n\nimport com.infinitepower.newquiz.core.util.kotlin.generateIncorrectNumberAnswers\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionApi\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.model.number.NumberTriviaQuestion\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass NumberTriviaQuestionRepositoryImpl @Inject constructor(\n    private val numberTriviaQuestionApi: NumberTriviaQuestionApi\n) : NumberTriviaQuestionRepository {\n    override suspend fun generateRandomQuestions(\n        size: Int,\n        minNumber: Int,\n        maxNumber: Int,\n        random: Random\n    ): List<NumberTriviaQuestion> {\n        val questionEntity = numberTriviaQuestionApi.getRandomQuestion(size, minNumber, maxNumber)\n        return questionEntity.toNumberTriviaQuestions()\n    }\n\n    override suspend fun generateWordleQuestion(random: Random): WordleWord {\n        val randomQuestion = generateRandomQuestions(1, 100, 10000, random).first()\n\n        return WordleWord(\n            word = randomQuestion.number.toString(),\n            textHelper = randomQuestion.question\n        )\n    }\n\n    override suspend fun generateMultiChoiceQuestion(size: Int, random: Random): List<MultiChoiceQuestion> {\n        val randomQuestion = generateRandomQuestions(size, 100, 10000, random)\n\n        return randomQuestion.map { question ->\n            val incorrectAnswers = generateIncorrectNumberAnswers(3, question.number)\n            val answers = (incorrectAnswers + question.number).shuffled(random)\n\n            MultiChoiceQuestion(\n                description = question.question,\n                answers = answers.map(Int::toString),\n                correctAns = answers.indexOf(question.number),\n                lang = QuestionLanguage.EN,\n                category = MultiChoiceBaseCategory.NumberTrivia,\n                type = MultiChoiceQuestionType.MULTIPLE,\n                difficulty = QuestionDifficulty.Easy\n            )\n        }\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/wordle/InvalidWordError.kt",
    "content": "package com.infinitepower.newquiz.data.repository.wordle\n\nsealed class InvalidWordError : IllegalArgumentException() {\n    data object Empty : InvalidWordError()\n    data object NotOnlyLetters : InvalidWordError()\n    data object NotOnlyDigits : InvalidWordError()\n    data object InvalidMathFormula : InvalidWordError()\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/repository/wordle/WordleRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.wordle\n\nimport android.content.Context\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.common.textWordleSupportedLang\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionRepository\nimport com.infinitepower.newquiz.domain.repository.wordle.WordleRepository\nimport com.infinitepower.newquiz.model.FlowResource\nimport com.infinitepower.newquiz.model.Resource\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.flow\nimport kotlinx.coroutines.withContext\nimport javax.inject.Inject\nimport javax.inject.Singleton\nimport kotlin.random.Random\n\n@Singleton\nclass WordleRepositoryImpl @Inject constructor(\n    @ApplicationContext private val context: Context,\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager,\n    private val mathQuizCoreRepository: MathQuizCoreRepository,\n    private val numberTriviaQuestionRepository: NumberTriviaQuestionRepository\n) : WordleRepository {\n    private val baseNumbers by lazy { 0..9 }\n\n    override suspend fun getAllWords(): Set<String> = withContext(Dispatchers.IO) {\n        val quizLanguage =\n            settingsDataStoreManager.getPreference(SettingsCommon.InfiniteWordleQuizLanguage)\n\n        val listRawId = textWordleSupportedLang.find { lang ->\n            lang.key == quizLanguage\n        }?.rawListId ?: throw NullPointerException(\"Wordle language not found\")\n\n        val wordleListInputStream = context.resources.openRawResource(listRawId)\n\n        try {\n            wordleListInputStream\n                .readBytes()\n                .decodeToString()\n                .split(\"\\r\\n\", \"\\n\")\n                .filter { it.length == 5 }\n                .map { it.uppercase().trim() }\n                .toSet()\n        } catch (e: Exception) {\n            throw e\n        } finally {\n            wordleListInputStream.close()\n        }\n    }\n\n    override fun generateRandomWord(\n        quizType: WordleQuizType,\n        random: Random\n    ): FlowResource<WordleWord> = flow {\n        try {\n            emit(Resource.Loading())\n\n            val randomWord = when (quizType) {\n                WordleQuizType.TEXT -> generateRandomTextWord(random = random)\n                WordleQuizType.NUMBER -> generateRandomNumberWord(random = random)\n                WordleQuizType.MATH_FORMULA -> {\n                    val formula = mathQuizCoreRepository.generateMathFormula(random = random)\n                    WordleWord(formula.fullFormula)\n                }\n\n                WordleQuizType.NUMBER_TRIVIA -> numberTriviaQuestionRepository.generateWordleQuestion(\n                    random = random\n                )\n            }\n\n            emit(Resource.Success(randomWord))\n        } catch (e: Exception) {\n            e.printStackTrace()\n            emit(Resource.Error(e.localizedMessage ?: \"A error occurred while getting word.\"))\n        }\n    }\n\n    override suspend fun generateRandomTextWord(random: Random): WordleWord {\n        val allWords = getAllWords().shuffled(random)\n\n        return WordleWord(allWords.random(random))\n    }\n\n    override suspend fun generateRandomTextWords(count: Int, random: Random): List<WordleWord> {\n        val allWords = getAllWords().shuffled(random)\n\n        return allWords\n            .take(count)\n            .map { word -> WordleWord(word) }\n    }\n\n    override suspend fun generateRandomNumberWord(\n        wordSize: Int,\n        random: Random\n    ): WordleWord {\n        val randomNumbers = List(wordSize) {\n            baseNumbers.random(random)\n        }\n\n        return WordleWord(randomNumbers.joinToString(\"\"))\n    }\n\n    override suspend fun isColorBlindEnabled(): Boolean {\n        return settingsDataStoreManager.getPreference(SettingsCommon.WordleColorBlindMode)\n    }\n\n    override suspend fun isLetterHintEnabled(): Boolean {\n        return settingsDataStoreManager.getPreference(SettingsCommon.WordleLetterHints)\n    }\n\n    override suspend fun isHardModeEnabled(): Boolean {\n        return settingsDataStoreManager.getPreference(SettingsCommon.WordleHardMode)\n    }\n\n    override suspend fun getWordleMaxRows(defaultMaxRow: Int?): Int {\n        if (defaultMaxRow == null) {\n            // If is row limited return row limit value else return int max value\n            val isRowLimited =\n                settingsDataStoreManager.getPreference(SettingsCommon.WordleInfiniteRowsLimited)\n            if (isRowLimited) return settingsDataStoreManager.getPreference(SettingsCommon.WordleInfiniteRowsLimit)\n\n            return Int.MAX_VALUE\n        }\n\n        return defaultMaxRow\n    }\n\n    @Suppress(\"ReturnCount\")\n    override fun validateWord(word: String, quizType: WordleQuizType): Result<Unit> {\n        if (word.isBlank()) return Result.failure(InvalidWordError.Empty)\n\n        when (quizType) {\n            WordleQuizType.TEXT -> {\n                if (!word.all(Char::isLetter)) {\n                    return Result.failure(InvalidWordError.NotOnlyLetters)\n                }\n            }\n\n            WordleQuizType.NUMBER, WordleQuizType.NUMBER_TRIVIA -> {\n                if (!word.all(Char::isDigit)) {\n                    return Result.failure(InvalidWordError.NotOnlyDigits)\n                }\n            }\n\n            WordleQuizType.MATH_FORMULA -> {\n                if (!mathQuizCoreRepository.validateFormula(word)) {\n                    return Result.failure(InvalidWordError.InvalidMathFormula)\n                }\n            }\n        }\n\n        return Result.success(Unit)\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/util/mappers/comparisonquiz/ComparisonQuizMapper.kt",
    "content": "package com.infinitepower.newquiz.data.util.mappers.comparisonquiz\n\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport java.net.URI\n\nfun ComparisonQuizItem.toEntity() = ComparisonQuizItemEntity(\n    title = title,\n    value = value,\n    imgUrl = imgUri.toASCIIString(),\n)\n\nfun ComparisonQuizItemEntity.toModel() = ComparisonQuizItem(\n    title = title,\n    value = value,\n    imgUri = URI.create(imgUrl)\n)\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/util/mappers/daily_challenge/DailyChallengeTaskMapper.kt",
    "content": "package com.infinitepower.newquiz.data.util.mappers.daily_challenge\n\nimport com.infinitepower.newquiz.core.database.model.DailyChallengeTaskEntity\nimport com.infinitepower.newquiz.data.repository.daily_challenge.util.getTitle\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport kotlinx.datetime.Instant\n\nfun DailyChallengeTask.toEntity(): DailyChallengeTaskEntity {\n    return DailyChallengeTaskEntity(\n        id = id,\n        diamondsReward = diamondsReward.toInt(),\n        experienceReward = experienceReward.toInt(),\n        isClaimed = isClaimed,\n        currentValue = currentValue.toInt(),\n        maxValue = maxValue.toInt(),\n        type = event.key,\n        startDate = dateRange.start.toEpochMilliseconds(),\n        endDate = dateRange.endInclusive.toEpochMilliseconds()\n    )\n}\n\nfun DailyChallengeTaskEntity.toModel(\n    comparisonQuizCategories: List<ComparisonQuizCategory>\n): DailyChallengeTask {\n    val startInstant = Instant.fromEpochMilliseconds(startDate)\n    val endInstant = Instant.fromEpochMilliseconds(endDate)\n\n    val dateRange = startInstant..endInstant\n\n    val type = GameEvent.fromKey(type)\n\n    return DailyChallengeTask(\n        id = id,\n        diamondsReward = diamondsReward.toUInt(),\n        experienceReward = experienceReward.toUInt(),\n        isClaimed = isClaimed,\n        dateRange = dateRange,\n        currentValue = currentValue.toUInt(),\n        maxValue = maxValue.toUInt(),\n        event = type,\n        title = type.getTitle(maxValue, comparisonQuizCategories)\n    )\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/util/mappers/maze/MazeQuizMappers.kt",
    "content": "package com.infinitepower.newquiz.data.util.mappers.maze\n\nimport com.infinitepower.newquiz.core.database.model.MazeQuizItemEntity\nimport com.infinitepower.newquiz.core.database.util.mappers.toEntity\nimport com.infinitepower.newquiz.core.database.util.mappers.toModel\nimport com.infinitepower.newquiz.data.util.mappers.comparisonquiz.toEntity\nimport com.infinitepower.newquiz.data.util.mappers.comparisonquiz.toModel\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport com.infinitepower.newquiz.model.wordle.WordleWord\n\nfun MazeQuiz.MazeItem.toEntity(): MazeQuizItemEntity = when (this) {\n    is MazeQuiz.MazeItem.Wordle -> MazeQuizItemEntity(\n        id = id,\n        mazeSeed = mazeSeed,\n        difficulty = difficulty,\n        played = played,\n        type = GameMode.WORDLE,\n        wordleItem = MazeQuizItemEntity.WordleEntity(\n            wordleWord = wordleWord.word,\n            wordleQuizType = wordleQuizType,\n            textHelper = wordleWord.textHelper\n        )\n    )\n\n    is MazeQuiz.MazeItem.MultiChoice -> MazeQuizItemEntity(\n        id = id,\n        mazeSeed = mazeSeed,\n        difficulty = difficulty,\n        played = played,\n        type = GameMode.MULTI_CHOICE,\n        multiChoiceQuestion = question.toEntity()\n    )\n\n    is MazeQuiz.MazeItem.ComparisonQuiz -> MazeQuizItemEntity(\n        id = id,\n        mazeSeed = mazeSeed,\n        difficulty = difficulty,\n        played = played,\n        type = GameMode.COMPARISON_QUIZ,\n        comparisonQuizQuestion = MazeQuizItemEntity.ComparisonQuizEntity(\n            category = question.categoryId,\n            comparisonMode = question.comparisonMode,\n            firstQuestion = question.questions.first.toEntity(),\n            secondQuestion = question.questions.second.toEntity()\n        )\n    )\n}\n\nfun MazeQuizItemEntity.toMazeQuizItem(): MazeQuiz.MazeItem = when (type) {\n    GameMode.WORDLE -> {\n        val wordleItem = this.wordleItem ?: throw NullPointerException(\"Wordle word is null\")\n        MazeQuiz.MazeItem.Wordle(\n            wordleWord = WordleWord(\n                word = wordleItem.wordleWord\n            ),\n            wordleQuizType = wordleItem.wordleQuizType,\n            id = id,\n            mazeSeed = mazeSeed,\n            difficulty = difficulty,\n            played = played\n        )\n    }\n\n    GameMode.MULTI_CHOICE -> {\n        val questionEntity = this.multiChoiceQuestion\n            ?: throw NullPointerException(\"Question is null\")\n        MazeQuiz.MazeItem.MultiChoice(questionEntity.toModel(), id, mazeSeed, difficulty, played)\n    }\n\n    GameMode.COMPARISON_QUIZ -> {\n        val questionEntity = this.comparisonQuizQuestion\n            ?: throw NullPointerException(\"Question is null\")\n\n        val firstQuestion = questionEntity.firstQuestion.toModel()\n        val secondQuestion = questionEntity.secondQuestion.toModel()\n\n        MazeQuiz.MazeItem.ComparisonQuiz(\n            question = ComparisonQuizQuestion(\n                questions = firstQuestion to secondQuestion,\n                categoryId = questionEntity.category,\n                comparisonMode = questionEntity.comparisonMode\n            ),\n            id = id,\n            mazeSeed = mazeSeed,\n            difficulty = difficulty,\n            played = played\n        )\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/util/translation/WordleTitleUtil.kt",
    "content": "package com.infinitepower.newquiz.data.util.translation\n\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.core.R as CoreR\n\nfun WordleQuizType?.getWordleTitle() = when (this) {\n    WordleQuizType.TEXT -> UiText.StringResource(CoreR.string.guess_the_word)\n    WordleQuizType.NUMBER -> UiText.StringResource(CoreR.string.guess_the_number)\n    WordleQuizType.MATH_FORMULA -> UiText.StringResource(CoreR.string.guess_math_formula)\n    WordleQuizType.NUMBER_TRIVIA -> UiText.StringResource(CoreR.string.number_trivia)\n    else -> UiText.StringResource(CoreR.string.wordle)\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/worker/UpdateGlobalEventDataWorker.kt",
    "content": "package com.infinitepower.newquiz.data.worker\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.OneTimeWorkRequestBuilder\nimport androidx.work.Operation\nimport androidx.work.WorkManager\nimport androidx.work.WorkerParameters\nimport androidx.work.workDataOf\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\n\n/**\n * This worker is responsible for updating the game event data.\n *\n * Updates daily challenge task data.\n * Update achievements data.\n */\n@HiltWorker\nclass UpdateGlobalEventDataWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    private val dailyChallengeRepository: DailyChallengeRepository\n) : CoroutineWorker(appContext, workerParams) {\n    companion object {\n        private const val EVENTS_KEY = \"events_key\"\n\n        fun enqueueWork(\n            workManager: WorkManager,\n            vararg event: GameEvent\n        ): Operation {\n            val eventsKey = event.map { it.key }.toTypedArray()\n\n            val workRequest = OneTimeWorkRequestBuilder<UpdateGlobalEventDataWorker>()\n                .setInputData(workDataOf(EVENTS_KEY to eventsKey))\n                .build()\n\n            return workManager.enqueue(workRequest)\n        }\n    }\n\n    override suspend fun doWork(): Result {\n        val eventsKey = inputData.getStringArray(EVENTS_KEY) ?: return Result.failure()\n\n        // Update daily challenge task data.\n        eventsKey.forEach { eventKey ->\n            // Get the event from the key.\n            try {\n                val event = GameEvent.fromKey(eventKey)\n\n                dailyChallengeRepository.completeTaskStep(event)\n            } catch (e: Exception) {\n                e.printStackTrace()\n            }\n        }\n\n        return Result.success()\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/worker/daily_challenge/VerifyDailyChallengeWorker.kt",
    "content": "package com.infinitepower.newquiz.data.worker.daily_challenge\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.ExistingPeriodicWorkPolicy\nimport androidx.work.Operation\nimport androidx.work.PeriodicWorkRequestBuilder\nimport androidx.work.WorkManager\nimport androidx.work.WorkerParameters\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\nimport kotlin.time.Duration.Companion.days\nimport kotlin.time.toJavaDuration\n\n@HiltWorker\nclass VerifyDailyChallengeWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    private val dailyChallengeRepository: DailyChallengeRepository,\n    private val remoteConfig: RemoteConfig\n) : CoroutineWorker(appContext, workerParams) {\n    companion object {\n        private const val WORK_NAME = \"VerifyDailyChallengeWorker\"\n\n        fun enqueueUniquePeriodicWork(workManager: WorkManager): Operation {\n            val verifyDailyChallengeWorker = PeriodicWorkRequestBuilder<VerifyDailyChallengeWorker>(\n                repeatInterval = 1.days.toJavaDuration()\n            ).build()\n\n            return workManager.enqueueUniquePeriodicWork(WORK_NAME, ExistingPeriodicWorkPolicy.KEEP, verifyDailyChallengeWorker)\n        }\n    }\n\n    override suspend fun doWork(): Result {\n        // Get the number of tasks to generate if the tasks are expired or not generated yet.\n        val tasksToGenerate = getTasksToGenerate()\n\n        // Check if the tasks are expired or not generated yet.\n        dailyChallengeRepository.checkAndGenerateTasksIfNeeded(tasksToGenerate)\n\n        return Result.success()\n    }\n\n    /**\n     * Get the number of tasks to generate in remote config.\n     * Default value is 5.\n     */\n    private fun getTasksToGenerate(): Int = remoteConfig.get(RemoteConfigValue.DAILY_CHALLENGE_TASKS_TO_GENERATE)\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/worker/maze/CleanMazeQuizWorker.kt",
    "content": "package com.infinitepower.newquiz.data.worker.maze\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.OneTimeWorkRequestBuilder\nimport androidx.work.WorkManager\nimport androidx.work.WorkerParameters\nimport com.infinitepower.newquiz.core.database.dao.MazeQuizDao\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\n@HiltWorker\nclass CleanMazeQuizWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    private val mazeQuizDao: MazeQuizDao\n) : CoroutineWorker(appContext, workerParams) {\n    companion object {\n        fun enqueue(workManager: WorkManager) {\n            val cleanSavedMazeRequest = OneTimeWorkRequestBuilder<CleanMazeQuizWorker>().build()\n\n            workManager.enqueue(cleanSavedMazeRequest)\n        }\n    }\n\n    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {\n        mazeQuizDao.deleteAll()\n\n        Result.success()\n    }\n}"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/worker/maze/GenerateMazeQuizWorker.kt",
    "content": "package com.infinitepower.newquiz.data.worker.maze\n\nimport android.content.Context\nimport android.util.Log\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.OneTimeWorkRequestBuilder\nimport androidx.work.WorkManager\nimport androidx.work.WorkerParameters\nimport androidx.work.workDataOf\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.util.kotlin.generateRandomUniqueItems\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.local.wordle.WordleCategories\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.CountryCapitalFlagsQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.FlagQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.GuessMathSolutionRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.LogoQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.MultiChoiceQuestionRepository\nimport com.infinitepower.newquiz.domain.repository.wordle.WordleRepository\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceCategory\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.wordle.WordleCategory\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport java.util.UUID\nimport kotlin.random.Random\nimport com.infinitepower.newquiz.core.R as CoreR\n\nprivate val multiChoiceBaseCategoriesIds: List<String> = listOf(\n    MultiChoiceBaseCategory.Random.id,\n    MultiChoiceBaseCategory.Logo.id,\n    MultiChoiceBaseCategory.Flag.id,\n    MultiChoiceBaseCategory.CountryCapitalFlags.id,\n    MultiChoiceBaseCategory.GuessMathSolution.id\n)\n\n@HiltWorker\nclass GenerateMazeQuizWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    private val mazeMathQuizRepository: MazeQuizRepository,\n    private val mathQuizCoreRepository: MathQuizCoreRepository,\n    private val flagQuizRepository: FlagQuizRepository,\n    private val logoQuizRepository: LogoQuizRepository,\n    private val wordleRepository: WordleRepository,\n    private val multiChoiceQuestionRepository: MultiChoiceQuestionRepository,\n    private val guessMathSolutionRepository: GuessMathSolutionRepository,\n    private val countryCapitalFlagsQuizRepository: CountryCapitalFlagsQuizRepository,\n    private val comparisonQuizRepository: ComparisonQuizRepository,\n    private val remoteConfig: RemoteConfig,\n    private val analyticsHelper: AnalyticsHelper,\n) : CoroutineWorker(appContext, workerParams) {\n    /**\n     * Game modes available to generate the maze quiz.\n     *\n     * Number trivia category is not supported because of api limitations\n     *\n     * @param name Name of the game mode\n     * @param categories Categories available to generate the maze quiz\n     */\n    sealed class GameModes<CategoryT : BaseCategory>(\n        val name: UiText,\n        val categories: List<CategoryT>\n    ) {\n        /**\n         * Get the offline game mode categories.\n         */\n        fun getOfflineCategories(): List<CategoryT> = categories.filterNot { category ->\n            category.requireInternetConnection\n        }\n\n        data object MultiChoice : GameModes<MultiChoiceCategory>(\n            name = UiText.StringResource(CoreR.string.multi_choice_quiz),\n            // Get only the categories that are in the multi choice base categories ids\n            categories = multiChoiceQuestionCategories.filter { category ->\n                multiChoiceBaseCategoriesIds.contains(category.id)\n            } + MultiChoiceCategory( // Add the normal categories to the options\n                id = MultiChoiceBaseCategory.Normal().categoryId,\n                name = UiText.StringResource(CoreR.string.others),\n                image = \"\"\n            )\n        )\n\n        data object Wordle : GameModes<WordleCategory>(\n            name = UiText.StringResource(CoreR.string.wordle),\n            categories = WordleCategories.allCategories.filter { category ->\n                category.wordleQuizType != WordleQuizType.NUMBER_TRIVIA\n            }\n        )\n    }\n\n    companion object {\n        private const val TAG = \"GenerateMazeQuizWorker\"\n\n        internal const val INPUT_SEED = \"INPUT_SEED\"\n        internal const val INPUT_QUESTION_SIZE = \"INPUT_QUESTION_SIZE\"\n        internal const val INPUT_MULTI_CHOICE_CATEGORIES = \"INPUT_MULTI_CHOICE_CATEGORIES\"\n        internal const val INPUT_WORDLE_QUIZ_TYPES = \"INPUT_WORDLE_QUIZ_TYPES\"\n        internal const val INPUT_COMPARISON_QUIZ_CATEGORIES = \"INPUT_COMPARISON_QUIZ_CATEGORIES\"\n\n        /**\n         * Enqueue a new work to generate a maze quiz.\n         *\n         * First it will clean the saved maze and then it will generate a new one.\n         *\n         * @param workManager WorkManager instance\n         * @param seed Seed to use in the random generator\n         * @param questionSize Question size to generate the maze\n         * @return The work id\n         */\n        fun enqueue(\n            workManager: WorkManager,\n            seed: Int?,\n            multiChoiceCategories: List<BaseCategory>,\n            wordleCategories: List<BaseCategory>,\n            comparisonQuizCategories: List<BaseCategory>,\n            questionSize: Int? = null\n        ): UUID {\n            val cleanSavedMazeRequest = OneTimeWorkRequestBuilder<CleanMazeQuizWorker>().build()\n\n            val multiChoiceCategoriesId = multiChoiceCategories.map { category ->\n                category.id\n            }.toTypedArray()\n\n            val wordleCategoriesIds = wordleCategories.map { category ->\n                category.id\n            }.toTypedArray()\n\n            val comparisonQuizCategoriesIds = comparisonQuizCategories.map { category ->\n                category.id\n            }.toTypedArray()\n\n            val generateMazeRequest = OneTimeWorkRequestBuilder<GenerateMazeQuizWorker>()\n                .setInputData(\n                    workDataOf(\n                        INPUT_SEED to seed,\n                        INPUT_MULTI_CHOICE_CATEGORIES to multiChoiceCategoriesId,\n                        INPUT_WORDLE_QUIZ_TYPES to wordleCategoriesIds,\n                        INPUT_COMPARISON_QUIZ_CATEGORIES to comparisonQuizCategoriesIds,\n                        INPUT_QUESTION_SIZE to questionSize\n                    )\n                ).build()\n\n            workManager\n                .beginWith(cleanSavedMazeRequest)\n                .then(generateMazeRequest)\n                .enqueue()\n\n            return generateMazeRequest.id\n        }\n    }\n\n    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {\n        val seed = inputData.getInt(INPUT_SEED, Random.nextInt())\n\n        val multiChoiceCategoriesIds = inputData.getStringArray(INPUT_MULTI_CHOICE_CATEGORIES)\n        requireNotNull(multiChoiceCategoriesIds)\n        Log.i(TAG, \"Multi choice categories: $multiChoiceCategoriesIds\")\n\n        val wordleCategoriesIds = inputData.getStringArray(INPUT_WORDLE_QUIZ_TYPES)\n        requireNotNull(wordleCategoriesIds)\n        Log.i(TAG, \"Wordle quiz types: $wordleCategoriesIds\")\n\n        val comparisonQuizCategoriesIds = inputData.getStringArray(INPUT_COMPARISON_QUIZ_CATEGORIES)\n        requireNotNull(comparisonQuizCategoriesIds)\n        Log.i(TAG, \"Comparison quiz categories: $comparisonQuizCategoriesIds\")\n\n        val questionSize = inputData.getInt(\n            INPUT_QUESTION_SIZE,\n            remoteConfig.get(RemoteConfigValue.MAZE_QUIZ_GENERATED_QUESTIONS)\n        )\n\n        // Random to use in all of the generators\n        val random = Random(seed)\n\n        // Get the questions size per mode, this is the size of the questions that will be generated per mode\n        val allCategoryCount = multiChoiceCategoriesIds.count() +\n                wordleCategoriesIds.count() +\n                comparisonQuizCategoriesIds.count()\n        val questionSizePerMode = questionSize / allCategoryCount\n\n        Log.i(\n            TAG,\n            \"Generating maze with seed: $seed, question size: $questionSize, question size per mode: $questionSizePerMode\"\n        )\n\n        val multiChoiceMazeQuestions = multiChoiceCategoriesIds.map { categoryId ->\n            val quizType = MultiChoiceBaseCategory.fromId(categoryId)\n\n            generateMultiChoiceMazeItems(\n                mazeSeed = seed,\n                questionSize = questionSizePerMode,\n                multiChoiceQuizType = quizType,\n                random = random\n            )\n        }\n\n        val wordleMazeQuestions = wordleCategoriesIds.map { categoryId ->\n            val wordleQuizType = WordleQuizType.valueOf(categoryId)\n\n            generateWordleMazeItems(\n                mazeSeed = seed,\n                questionSize = questionSizePerMode,\n                wordleQuizType = wordleQuizType,\n                random = random\n            )\n        }\n\n        val comparisonQuizCategories = comparisonQuizRepository.getCategories()\n        val comparisonMazeQuestions = comparisonQuizCategoriesIds.mapNotNull { categoryId ->\n            val category = comparisonQuizCategories.find { it.id == categoryId }\n            if (category == null) return@mapNotNull null\n\n            generateComparisonMazeItems(\n                category = category,\n                mazeSeed = seed,\n                questionSize = questionSizePerMode,\n                random = random\n            )\n        }\n\n        val allMazeQuestions = (multiChoiceMazeQuestions + wordleMazeQuestions + comparisonMazeQuestions)\n            .flatten()\n            .shuffled(random)\n\n        mazeMathQuizRepository.insertItems(allMazeQuestions)\n\n        val questionsCount = mazeMathQuizRepository.countAllItems()\n\n        check(questionsCount == allMazeQuestions.count()) {\n            \"Maze saved questions: $questionsCount is not equal to generated questions: ${allMazeQuestions.count()}\"\n        }\n\n        Log.d(TAG, \"Generated $questionsCount questions\")\n\n        analyticsHelper.logEvent(AnalyticsEvent.CreateMaze(seed, questionsCount))\n\n        Result.success()\n    }\n\n    private suspend fun generateMultiChoiceMazeItems(\n        mazeSeed: Int,\n        questionSize: Int,\n        multiChoiceQuizType: MultiChoiceBaseCategory,\n        difficulty: QuestionDifficulty = QuestionDifficulty.Easy,\n        random: Random = Random\n    ): List<MazeQuiz.MazeItem> {\n        val questions = when (multiChoiceQuizType) {\n            is MultiChoiceBaseCategory.Normal -> multiChoiceQuestionRepository.getRandomQuestions(\n                amount = questionSize,\n                random = random,\n                category = multiChoiceQuizType\n            )\n\n            is MultiChoiceBaseCategory.Logo -> logoQuizRepository.getRandomQuestions(\n                amount = questionSize,\n                random = random,\n                category = multiChoiceQuizType\n            )\n\n            is MultiChoiceBaseCategory.Flag -> flagQuizRepository.getRandomQuestions(\n                amount = questionSize,\n                random = random,\n                category = multiChoiceQuizType\n            )\n\n            is MultiChoiceBaseCategory.GuessMathSolution -> guessMathSolutionRepository.getRandomQuestions(\n                amount = questionSize,\n                random = random,\n                category = multiChoiceQuizType\n            )\n\n            is MultiChoiceBaseCategory.CountryCapitalFlags -> countryCapitalFlagsQuizRepository.getRandomQuestions(\n                amount = questionSize,\n                random = random,\n                category = multiChoiceQuizType\n            )\n\n            // Temporary disabled because of api limitations\n            is MultiChoiceBaseCategory.NumberTrivia -> error(\"Number trivia is not supported\")\n        }\n\n        return questions.map { question ->\n            MazeQuiz.MazeItem.MultiChoice(\n                mazeSeed = mazeSeed,\n                question = question,\n                difficulty = difficulty\n            )\n        }\n    }\n\n    private suspend fun generateWordleMazeItems(\n        mazeSeed: Int,\n        questionSize: Int,\n        wordleQuizType: WordleQuizType,\n        difficulty: QuestionDifficulty = QuestionDifficulty.Easy,\n        random: Random = Random\n    ): List<MazeQuiz.MazeItem> {\n        require(wordleQuizType != WordleQuizType.NUMBER_TRIVIA) {\n            \"Number trivia is not supported\"\n        }\n\n        if (wordleQuizType == WordleQuizType.TEXT) {\n            return wordleRepository.generateRandomTextWords(\n                count = questionSize,\n                random = random\n            ).map { word ->\n                MazeQuiz.MazeItem.Wordle(\n                    mazeSeed = mazeSeed,\n                    wordleWord = word,\n                    wordleQuizType = wordleQuizType,\n                    difficulty = difficulty\n                )\n            }\n        }\n\n        return generateRandomUniqueItems(\n            itemCount = questionSize,\n            generator = {\n                when (wordleQuizType) {\n                    WordleQuizType.NUMBER -> wordleRepository.generateRandomNumberWord(random = random)\n                    WordleQuizType.MATH_FORMULA -> {\n                        val formula = mathQuizCoreRepository.generateMathFormula(\n                            difficulty = difficulty,\n                            random = random\n                        )\n\n                        WordleWord(formula.fullFormula)\n                    }\n\n                    else -> error(\"Wordle quiz type not supported\")\n                }\n            }\n        ).map { word ->\n            MazeQuiz.MazeItem.Wordle(\n                mazeSeed = mazeSeed,\n                wordleWord = word,\n                wordleQuizType = wordleQuizType,\n                difficulty = difficulty\n            )\n        }\n    }\n\n    private suspend fun generateComparisonMazeItems(\n        category: ComparisonQuizCategory,\n        mazeSeed: Int,\n        questionSize: Int,\n        random: Random = Random\n    ): List<MazeQuiz.MazeItem> {\n        // Each question has two items, so we need to request twice as many questions\n        val requestSize = questionSize * 2\n\n        return comparisonQuizRepository.getQuestions(\n            category = category,\n            size = requestSize,\n            random = random\n        ).chunked(2) { items ->\n            val firstQuestion = items.first()\n            val secondQuestion = items.last()\n\n            MazeQuiz.MazeItem.ComparisonQuiz(\n                mazeSeed = mazeSeed,\n                question = ComparisonQuizQuestion(\n                    questions = firstQuestion to secondQuestion,\n                    categoryId = category.id,\n                    comparisonMode = ComparisonMode.GREATER // In future we can make this random\n                ),\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/infinitepower/newquiz/data/worker/multichoicequiz/DownloadMultiChoiceQuestionsWorker.kt",
    "content": "package com.infinitepower.newquiz.data.worker.multichoicequiz\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.WorkerParameters\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.MultiChoiceQuestionRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\n\n@HiltWorker\nclass DownloadMultiChoiceQuestionsWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    private val savedQuestionsRepository: SavedMultiChoiceQuestionsRepository,\n    private val questionRepository: MultiChoiceQuestionRepository\n) : CoroutineWorker(appContext, workerParams) {\n    override suspend fun doWork(): Result {\n        val allSavedQuestions = savedQuestionsRepository.getQuestions()\n\n        val questions = questionRepository\n            .getRandomQuestions(amount = 50, category = MultiChoiceBaseCategory.Normal())\n            .filter { it !in allSavedQuestions }\n\n        savedQuestionsRepository.insertQuestions(questions)\n\n        return Result.success()\n    }\n}\n"
  },
  {
    "path": "data/src/main/res/raw/all_countries.json",
    "content": "[\n  {\n    \"countryCode\": \"AF\",\n    \"capital\": \"Kabul\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Afghanistan\",\n    \"continent\": \"Asia\",\n    \"population\": 40218234,\n    \"area\": 652230\n  },\n  {\n    \"countryCode\": \"AL\",\n    \"capital\": \"Tirana\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Albania\",\n    \"continent\": \"Europe\",\n    \"population\": 2837743,\n    \"area\": 28748\n  },\n  {\n    \"countryCode\": \"DZ\",\n    \"capital\": \"Algiers\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Algeria\",\n    \"continent\": \"Africa\",\n    \"population\": 44700000,\n    \"area\": 2381741\n  },\n  {\n    \"countryCode\": \"AD\",\n    \"capital\": \"Andorra la Vella\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Andorra\",\n    \"continent\": \"Europe\",\n    \"population\": 77265,\n    \"area\": 468\n  },\n  {\n    \"countryCode\": \"AO\",\n    \"capital\": \"Luanda\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Angola\",\n    \"continent\": \"Africa\",\n    \"population\": 32866268,\n    \"area\": 1246700\n  },\n  {\n    \"countryCode\": \"AG\",\n    \"capital\": \"St. John's\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Antigua and Barbuda\",\n    \"continent\": \"North America\",\n    \"population\": 97928,\n    \"area\": 442\n  },\n  {\n    \"countryCode\": \"AR\",\n    \"capital\": \"Buenos Aires\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Argentina\",\n    \"continent\": \"South America\",\n    \"population\": 45376763,\n    \"area\": 2780400\n  },\n  {\n    \"countryCode\": \"AM\",\n    \"capital\": \"Yerevan\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Armenia\",\n    \"continent\": \"Asia\",\n    \"population\": 2963234,\n    \"area\": 29743\n  },\n  {\n    \"countryCode\": \"AU\",\n    \"capital\": \"Canberra\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Australia\",\n    \"continent\": \"Oceania\",\n    \"population\": 25687041,\n    \"area\": 7692024\n  },\n  {\n    \"countryCode\": \"AT\",\n    \"capital\": \"Vienna\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Austria\",\n    \"continent\": \"Europe\",\n    \"population\": 8917205,\n    \"area\": 83871\n  },\n  {\n    \"countryCode\": \"AZ\",\n    \"capital\": \"Baku\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Azerbaijan\",\n    \"continent\": \"Asia\",\n    \"population\": 10110116,\n    \"area\": 86600\n  },\n  {\n    \"countryCode\": \"BS\",\n    \"capital\": \"Nassau\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Bahamas\",\n    \"continent\": \"North America\",\n    \"population\": 393248,\n    \"area\": 13943\n  },\n  {\n    \"countryCode\": \"BH\",\n    \"capital\": \"Manama\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Bahrain\",\n    \"continent\": \"Asia\",\n    \"population\": 1701583,\n    \"area\": 765\n  },\n  {\n    \"countryCode\": \"BD\",\n    \"capital\": \"Dhaka\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Bangladesh\",\n    \"continent\": \"Asia\",\n    \"population\": 164689383,\n    \"area\": 147570\n  },\n  {\n    \"countryCode\": \"BB\",\n    \"capital\": \"Bridgetown\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Barbados\",\n    \"continent\": \"North America\",\n    \"population\": 287371,\n    \"area\": 430\n  },\n  {\n    \"countryCode\": \"BY\",\n    \"capital\": \"Minsk\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Belarus\",\n    \"continent\": \"Europe\",\n    \"population\": 9398861,\n    \"area\": 207600\n  },\n  {\n    \"countryCode\": \"BE\",\n    \"capital\": \"Brussels\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Belgium\",\n    \"continent\": \"Europe\",\n    \"population\": 11555997,\n    \"area\": 30528\n  },\n  {\n    \"countryCode\": \"BZ\",\n    \"capital\": \"Belmopan\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Belize\",\n    \"continent\": \"North America\",\n    \"population\": 397621,\n    \"area\": 22966\n  },\n  {\n    \"countryCode\": \"BJ\",\n    \"capital\": \"Porto-Novo\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Benin\",\n    \"continent\": \"Africa\",\n    \"population\": 12123198,\n    \"area\": 112622\n  },\n  {\n    \"countryCode\": \"BT\",\n    \"capital\": \"Thimphu\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Bhutan\",\n    \"continent\": \"Asia\",\n    \"population\": 771612,\n    \"area\": 38394\n  },\n  {\n    \"countryCode\": \"BO\",\n    \"capital\": \"La Paz\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Bolivia\",\n    \"continent\": \"South America\",\n    \"population\": 11673029,\n    \"area\": 1098581\n  },\n  {\n    \"countryCode\": \"BA\",\n    \"capital\": \"Sarajevo\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Bosnia and Herzegovina\",\n    \"continent\": \"Europe\",\n    \"population\": 3280815,\n    \"area\": 51209\n  },\n  {\n    \"countryCode\": \"BW\",\n    \"capital\": \"Gaborone\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Botswana\",\n    \"continent\": \"Africa\",\n    \"population\": 2351625,\n    \"area\": 582000\n  },\n  {\n    \"countryCode\": \"BR\",\n    \"capital\": \"Brasília\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Brazil\",\n    \"continent\": \"South America\",\n    \"population\": 212559409,\n    \"area\": 8515767\n  },\n  {\n    \"countryCode\": \"BN\",\n    \"capital\": \"Bandar Seri Begawan\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Brunei\",\n    \"continent\": \"Asia\",\n    \"population\": 437483,\n    \"area\": 5765\n  },\n  {\n    \"countryCode\": \"BG\",\n    \"capital\": \"Sofia\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Bulgaria\",\n    \"continent\": \"Europe\",\n    \"population\": 6927288,\n    \"area\": 110879\n  },\n  {\n    \"countryCode\": \"BF\",\n    \"capital\": \"Ouagadougou\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Burkina Faso\",\n    \"continent\": \"Africa\",\n    \"population\": 20903278,\n    \"area\": 272967\n  },\n  {\n    \"countryCode\": \"BI\",\n    \"capital\": \"Bujumbura\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Burundi\",\n    \"continent\": \"Africa\",\n    \"population\": 11890781,\n    \"area\": 27834\n  },\n  {\n    \"countryCode\": \"KH\",\n    \"capital\": \"Phnom Penh\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Cambodia\",\n    \"continent\": \"Asia\",\n    \"population\": 16718971,\n    \"area\": 181035\n  },\n  {\n    \"countryCode\": \"CM\",\n    \"capital\": \"Yaoundé\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Cameroon\",\n    \"continent\": \"Africa\",\n    \"population\": 26545864,\n    \"area\": 475442\n  },\n  {\n    \"countryCode\": \"CA\",\n    \"capital\": \"Ottawa\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Canada\",\n    \"continent\": \"North America\",\n    \"population\": 38005238,\n    \"area\": 9984670\n  },\n  {\n    \"countryCode\": \"CV\",\n    \"capital\": \"Praia\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Cape Verde\",\n    \"continent\": \"Africa\",\n    \"population\": 555988,\n    \"area\": 4033\n  },\n  {\n    \"countryCode\": \"CF\",\n    \"capital\": \"Bangui\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Central African Republic\",\n    \"continent\": \"Africa\",\n    \"population\": 4829764,\n    \"area\": 622984\n  },\n  {\n    \"countryCode\": \"TD\",\n    \"capital\": \"N'Djamena\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Chad\",\n    \"continent\": \"Africa\",\n    \"population\": 16425859,\n    \"area\": 1284000\n  },\n  {\n    \"countryCode\": \"CL\",\n    \"capital\": \"Santiago\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Chile\",\n    \"continent\": \"South America\",\n    \"population\": 19116209,\n    \"area\": 756102\n  },\n  {\n    \"countryCode\": \"CN\",\n    \"capital\": \"Beijing\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"China\",\n    \"continent\": \"Asia\",\n    \"population\": 1402112000,\n    \"area\": 9706961\n  },\n  {\n    \"countryCode\": \"CO\",\n    \"capital\": \"Bogotá\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Colombia\",\n    \"continent\": \"South America\",\n    \"population\": 50882884,\n    \"area\": 1141748\n  },\n  {\n    \"countryCode\": \"KM\",\n    \"capital\": \"Moroni\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Comoros\",\n    \"continent\": \"Africa\",\n    \"population\": 869595,\n    \"area\": 1862\n  },\n  {\n    \"countryCode\": \"CD\",\n    \"capital\": \"Kinshasa\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Democratic Republic of the Congo\",\n    \"continent\": \"Africa\",\n    \"population\": 108407721,\n    \"area\": 2344858\n  },\n  {\n    \"countryCode\": \"CG\",\n    \"capital\": \"Brazzaville\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Republic of the Congo\",\n    \"continent\": \"Africa\",\n    \"population\": 5657000,\n    \"area\": 342000\n  },\n  {\n    \"countryCode\": \"CR\",\n    \"capital\": \"San José\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Costa Rica\",\n    \"continent\": \"North America\",\n    \"population\": 5094114,\n    \"area\": 51100\n  },\n  {\n    \"countryCode\": \"CI\",\n    \"capital\": \"Yamoussoukro\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Côte d'Ivoire\",\n    \"continent\": \"Africa\",\n    \"population\": 26378275,\n    \"area\": 322463\n  },\n  {\n    \"countryCode\": \"HR\",\n    \"capital\": \"Zagreb\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Croatia\",\n    \"continent\": \"Europe\",\n    \"population\": 4047200,\n    \"area\": 56594\n  },\n  {\n    \"countryCode\": \"CU\",\n    \"capital\": \"Havana\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Cuba\",\n    \"continent\": \"North America\",\n    \"population\": 11326616,\n    \"area\": 109884\n  },\n  {\n    \"countryCode\": \"CY\",\n    \"capital\": \"Nicosia\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Cyprus\",\n    \"continent\": \"Asia\",\n    \"population\": 1207361,\n    \"area\": 9251\n  },\n  {\n    \"countryCode\": \"CZ\",\n    \"capital\": \"Prague\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Czech Republic\",\n    \"continent\": \"Europe\",\n    \"population\": 10698896,\n    \"area\": 78865\n  },\n  {\n    \"countryCode\": \"DK\",\n    \"capital\": \"Copenhagen\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Denmark\",\n    \"continent\": \"Europe\",\n    \"population\": 5831404,\n    \"area\": 43094\n  },\n  {\n    \"countryCode\": \"DJ\",\n    \"capital\": \"Djibouti\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Djibouti\",\n    \"continent\": \"Africa\",\n    \"population\": 988002,\n    \"area\": 23200\n  },\n  {\n    \"countryCode\": \"DM\",\n    \"capital\": \"Roseau\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Dominica\",\n    \"continent\": \"North America\",\n    \"population\": 71991,\n    \"area\": 751\n  },\n  {\n    \"countryCode\": \"DO\",\n    \"capital\": \"Santo Domingo\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Dominican Republic\",\n    \"continent\": \"North America\",\n    \"population\": 10847904,\n    \"area\": 48671\n  },\n  {\n    \"countryCode\": \"EC\",\n    \"capital\": \"Quito\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Ecuador\",\n    \"continent\": \"South America\",\n    \"population\": 17643060,\n    \"area\": 276841\n  },\n  {\n    \"countryCode\": \"EG\",\n    \"capital\": \"Cairo\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Egypt\",\n    \"continent\": \"Africa\",\n    \"population\": 102334403,\n    \"area\": 1002450\n  },\n  {\n    \"countryCode\": \"SV\",\n    \"capital\": \"San Salvador\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"El Salvador\",\n    \"continent\": \"North America\",\n    \"population\": 6486201,\n    \"area\": 21041\n  },\n  {\n    \"countryCode\": \"GQ\",\n    \"capital\": \"Malabo\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Equatorial Guinea\",\n    \"continent\": \"Africa\",\n    \"population\": 1402985,\n    \"area\": 28051\n  },\n  {\n    \"countryCode\": \"ER\",\n    \"capital\": \"Asmara\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Eritrea\",\n    \"continent\": \"Africa\",\n    \"population\": 5352000,\n    \"area\": 117600\n  },\n  {\n    \"countryCode\": \"EE\",\n    \"capital\": \"Tallinn\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Estonia\",\n    \"continent\": \"Europe\",\n    \"population\": 1331057,\n    \"area\": 45227\n  },\n  {\n    \"countryCode\": \"ET\",\n    \"capital\": \"Addis Ababa\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Ethiopia\",\n    \"continent\": \"Africa\",\n    \"population\": 114963583,\n    \"area\": 1104300\n  },\n  {\n    \"countryCode\": \"FJ\",\n    \"capital\": \"Suva\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Fiji\",\n    \"continent\": \"Oceania\",\n    \"population\": 896444,\n    \"area\": 18272\n  },\n  {\n    \"countryCode\": \"FI\",\n    \"capital\": \"Helsinki\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Finland\",\n    \"continent\": \"Europe\",\n    \"population\": 5530719,\n    \"area\": 338424\n  },\n  {\n    \"countryCode\": \"FR\",\n    \"capital\": \"Paris\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"France\",\n    \"continent\": \"Europe\",\n    \"population\": 67391582,\n    \"area\": 551695\n  },\n  {\n    \"countryCode\": \"GA\",\n    \"capital\": \"Libreville\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Gabon\",\n    \"continent\": \"Africa\",\n    \"population\": 2225728,\n    \"area\": 267668\n  },\n  {\n    \"countryCode\": \"GM\",\n    \"capital\": \"Banjul\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Gambia\",\n    \"continent\": \"Africa\",\n    \"population\": 2416664,\n    \"area\": 10689\n  },\n  {\n    \"countryCode\": \"GE\",\n    \"capital\": \"Tbilisi\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Georgia\",\n    \"continent\": \"Asia\",\n    \"population\": 3714000,\n    \"area\": 69700\n  },\n  {\n    \"countryCode\": \"DE\",\n    \"capital\": \"Berlin\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Germany\",\n    \"continent\": \"Europe\",\n    \"population\": 83240525,\n    \"area\": 357114\n  },\n  {\n    \"countryCode\": \"GH\",\n    \"capital\": \"Accra\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Ghana\",\n    \"continent\": \"Africa\",\n    \"population\": 31072945,\n    \"area\": 238533\n  },\n  {\n    \"countryCode\": \"GR\",\n    \"capital\": \"Athens\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Greece\",\n    \"continent\": \"Europe\",\n    \"population\": 10715549,\n    \"area\": 131990\n  },\n  {\n    \"countryCode\": \"GD\",\n    \"capital\": \"St. George's\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Grenada\",\n    \"continent\": \"North America\",\n    \"population\": 112519,\n    \"area\": 344\n  },\n  {\n    \"countryCode\": \"GT\",\n    \"capital\": \"Guatemala City\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Guatemala\",\n    \"continent\": \"North America\",\n    \"population\": 16858333,\n    \"area\": 108889\n  },\n  {\n    \"countryCode\": \"GN\",\n    \"capital\": \"Conakry\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Guinea\",\n    \"continent\": \"Africa\",\n    \"population\": 13132792,\n    \"area\": 245857\n  },\n  {\n    \"countryCode\": \"GW\",\n    \"capital\": \"Bissau\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Guinea-Bissau\",\n    \"continent\": \"Africa\",\n    \"population\": 1967998,\n    \"area\": 36125\n  },\n  {\n    \"countryCode\": \"GY\",\n    \"capital\": \"Georgetown\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Guyana\",\n    \"continent\": \"South America\",\n    \"population\": 786559,\n    \"area\": 214969\n  },\n  {\n    \"countryCode\": \"HT\",\n    \"capital\": \"Port-au-Prince\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Haiti\",\n    \"continent\": \"North America\",\n    \"population\": 11402533,\n    \"area\": 27750\n  },\n  {\n    \"countryCode\": \"HN\",\n    \"capital\": \"Tegucigalpa\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Honduras\",\n    \"continent\": \"North America\",\n    \"population\": 9904608,\n    \"area\": 112492\n  },\n  {\n    \"countryCode\": \"HU\",\n    \"capital\": \"Budapest\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Hungary\",\n    \"continent\": \"Europe\",\n    \"population\": 9749763,\n    \"area\": 93028\n  },\n  {\n    \"countryCode\": \"IS\",\n    \"capital\": \"Reykjavik\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Iceland\",\n    \"continent\": \"Europe\",\n    \"population\": 366425,\n    \"area\": 103000\n  },\n  {\n    \"countryCode\": \"IN\",\n    \"capital\": \"New Delhi\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"India\",\n    \"continent\": \"Asia\",\n    \"population\": 1380004385,\n    \"area\": 3287590\n  },\n  {\n    \"countryCode\": \"ID\",\n    \"capital\": \"Jakarta\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Indonesia\",\n    \"continent\": \"Asia\",\n    \"population\": 273523621,\n    \"area\": 1904569\n  },\n  {\n    \"countryCode\": \"IR\",\n    \"capital\": \"Tehran\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Iran\",\n    \"continent\": \"Asia\",\n    \"population\": 83992953,\n    \"area\": 1648195\n  },\n  {\n    \"countryCode\": \"IQ\",\n    \"capital\": \"Baghdad\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Iraq\",\n    \"continent\": \"Asia\",\n    \"population\": 40222503,\n    \"area\": 438317\n  },\n  {\n    \"countryCode\": \"IE\",\n    \"capital\": \"Dublin\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Ireland\",\n    \"continent\": \"Europe\",\n    \"population\": 4994724,\n    \"area\": 70273\n  },\n  {\n    \"countryCode\": \"IL\",\n    \"capital\": \"Jerusalem\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Israel\",\n    \"continent\": \"Asia\",\n    \"population\": 9216900,\n    \"area\": 20770\n  },\n  {\n    \"countryCode\": \"IT\",\n    \"capital\": \"Rome\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Italy\",\n    \"continent\": \"Europe\",\n    \"population\": 59554023,\n    \"area\": 301336\n  },\n  {\n    \"countryCode\": \"JM\",\n    \"capital\": \"Kingston\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Jamaica\",\n    \"continent\": \"North America\",\n    \"population\": 2961161,\n    \"area\": 10991\n  },\n  {\n    \"countryCode\": \"JP\",\n    \"capital\": \"Tokyo\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Japan\",\n    \"continent\": \"Asia\",\n    \"population\": 125836021,\n    \"area\": 377930\n  },\n  {\n    \"countryCode\": \"JO\",\n    \"capital\": \"Amman\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Jordan\",\n    \"continent\": \"Asia\",\n    \"population\": 10203140,\n    \"area\": 89342\n  },\n  {\n    \"countryCode\": \"KZ\",\n    \"capital\": \"Nur-Sultan\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Kazakhstan\",\n    \"continent\": \"Asia\",\n    \"population\": 18754440,\n    \"area\": 2724900\n  },\n  {\n    \"countryCode\": \"KE\",\n    \"capital\": \"Nairobi\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Kenya\",\n    \"continent\": \"Africa\",\n    \"population\": 53771300,\n    \"area\": 580367\n  },\n  {\n    \"countryCode\": \"KI\",\n    \"capital\": \"Tarawa\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Kiribati\",\n    \"continent\": \"Oceania\",\n    \"population\": 119446,\n    \"area\": 811\n  },\n  {\n    \"countryCode\": \"KP\",\n    \"capital\": \"Pyongyang\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"North Korea\",\n    \"continent\": \"Asia\",\n    \"population\": 25778815,\n    \"area\": 120538\n  },\n  {\n    \"countryCode\": \"KR\",\n    \"capital\": \"Seoul\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"South Korea\",\n    \"continent\": \"Asia\",\n    \"population\": 51780579,\n    \"area\": 100210\n  },\n  {\n    \"countryCode\": \"KW\",\n    \"capital\": \"Kuwait City\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Kuwait\",\n    \"continent\": \"Asia\",\n    \"population\": 4270563,\n    \"area\": 17818\n  },\n  {\n    \"countryCode\": \"KG\",\n    \"capital\": \"Bishkek\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Kyrgyzstan\",\n    \"continent\": \"Asia\",\n    \"population\": 6591600,\n    \"area\": 199951\n  },\n  {\n    \"countryCode\": \"LA\",\n    \"capital\": \"Vientiane\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Laos\",\n    \"continent\": \"Asia\",\n    \"population\": 7275556,\n    \"area\": 236800\n  },\n  {\n    \"countryCode\": \"LV\",\n    \"capital\": \"Riga\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Latvia\",\n    \"continent\": \"Europe\",\n    \"population\": 1901548,\n    \"area\": 64559\n  },\n  {\n    \"countryCode\": \"LB\",\n    \"capital\": \"Beirut\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Lebanon\",\n    \"continent\": \"Asia\",\n    \"population\": 6825442,\n    \"area\": 10452\n  },\n  {\n    \"countryCode\": \"LS\",\n    \"capital\": \"Maseru\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Lesotho\",\n    \"continent\": \"Africa\",\n    \"population\": 2142252,\n    \"area\": 30355\n  },\n  {\n    \"countryCode\": \"LR\",\n    \"capital\": \"Monrovia\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Liberia\",\n    \"continent\": \"Africa\",\n    \"population\": 5057677,\n    \"area\": 111369\n  },\n  {\n    \"countryCode\": \"LY\",\n    \"capital\": \"Tripoli\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Libya\",\n    \"continent\": \"Africa\",\n    \"population\": 6871287,\n    \"area\": 1759540\n  },\n  {\n    \"countryCode\": \"LI\",\n    \"capital\": \"Vaduz\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Liechtenstein\",\n    \"continent\": \"Europe\",\n    \"population\": 38137,\n    \"area\": 160\n  },\n  {\n    \"countryCode\": \"LT\",\n    \"capital\": \"Vilnius\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Lithuania\",\n    \"continent\": \"Europe\",\n    \"population\": 2794700,\n    \"area\": 65300\n  },\n  {\n    \"countryCode\": \"LU\",\n    \"capital\": \"Luxembourg City\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Luxembourg\",\n    \"continent\": \"Europe\",\n    \"population\": 632275,\n    \"area\": 2586\n  },\n  {\n    \"countryCode\": \"MG\",\n    \"capital\": \"Antananarivo\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Madagascar\",\n    \"continent\": \"Africa\",\n    \"population\": 27691019,\n    \"area\": 587041\n  },\n  {\n    \"countryCode\": \"MW\",\n    \"capital\": \"Lilongwe\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Malawi\",\n    \"continent\": \"Africa\",\n    \"population\": 19129955,\n    \"area\": 118484\n  },\n  {\n    \"countryCode\": \"MY\",\n    \"capital\": \"Kuala Lumpur\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Malaysia\",\n    \"continent\": \"Asia\",\n    \"population\": 32365998,\n    \"area\": 330803\n  },\n  {\n    \"countryCode\": \"MV\",\n    \"capital\": \"Male\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Maldives\",\n    \"continent\": \"Asia\",\n    \"population\": 540542,\n    \"area\": 300\n  },\n  {\n    \"countryCode\": \"ML\",\n    \"capital\": \"Bamako\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Mali\",\n    \"continent\": \"Africa\",\n    \"population\": 20250834,\n    \"area\": 1240192\n  },\n  {\n    \"countryCode\": \"MT\",\n    \"capital\": \"Valletta\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Malta\",\n    \"continent\": \"Europe\",\n    \"population\": 525285,\n    \"area\": 316\n  },\n  {\n    \"countryCode\": \"MH\",\n    \"capital\": \"Majuro\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Marshall Islands\",\n    \"continent\": \"Oceania\",\n    \"population\": 59194,\n    \"area\": 181\n  },\n  {\n    \"countryCode\": \"MR\",\n    \"capital\": \"Nouakchott\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Mauritania\",\n    \"continent\": \"Africa\",\n    \"population\": 4649660,\n    \"area\": 1030700\n  },\n  {\n    \"countryCode\": \"MU\",\n    \"capital\": \"Port Louis\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Mauritius\",\n    \"continent\": \"Africa\",\n    \"population\": 1265740,\n    \"area\": 2040\n  },\n  {\n    \"countryCode\": \"MX\",\n    \"capital\": \"Mexico City\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Mexico\",\n    \"continent\": \"North America\",\n    \"population\": 128932753,\n    \"area\": 1964375\n  },\n  {\n    \"countryCode\": \"FM\",\n    \"capital\": \"Palikir\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Micronesia\",\n    \"continent\": \"Oceania\",\n    \"population\": 115021,\n    \"area\": 702\n  },\n  {\n    \"countryCode\": \"MD\",\n    \"capital\": \"Chisinau\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Moldova\",\n    \"continent\": \"Europe\",\n    \"population\": 2617820,\n    \"area\": 33846\n  },\n  {\n    \"countryCode\": \"MC\",\n    \"capital\": \"Monaco\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Monaco\",\n    \"continent\": \"Europe\",\n    \"population\": 39244,\n    \"area\": 2.02\n  },\n  {\n    \"countryCode\": \"MN\",\n    \"capital\": \"Ulaanbaatar\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Mongolia\",\n    \"continent\": \"Asia\",\n    \"population\": 3278292,\n    \"area\": 1564110\n  },\n  {\n    \"countryCode\": \"ME\",\n    \"capital\": \"Podgorica\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Montenegro\",\n    \"continent\": \"Europe\",\n    \"population\": 621718,\n    \"area\": 13812\n  },\n  {\n    \"countryCode\": \"MA\",\n    \"capital\": \"Rabat\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Morocco\",\n    \"continent\": \"Africa\",\n    \"population\": 36910558,\n    \"area\": 446550\n  },\n  {\n    \"countryCode\": \"MZ\",\n    \"capital\": \"Maputo\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Mozambique\",\n    \"continent\": \"Africa\",\n    \"population\": 31255435,\n    \"area\": 801590\n  },\n  {\n    \"countryCode\": \"MM\",\n    \"capital\": \"Naypyidaw\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Myanmar (Burma)\",\n    \"continent\": \"Asia\",\n    \"population\": 54409794,\n    \"area\": 676578\n  },\n  {\n    \"countryCode\": \"NA\",\n    \"capital\": \"Windhoek\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Namibia\",\n    \"continent\": \"Africa\",\n    \"population\": 2540916,\n    \"area\": 825615\n  },\n  {\n    \"countryCode\": \"NR\",\n    \"capital\": \"Yaren\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Nauru\",\n    \"continent\": \"Oceania\",\n    \"population\": 10834,\n    \"area\": 21\n  },\n  {\n    \"countryCode\": \"NP\",\n    \"capital\": \"Kathmandu\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Nepal\",\n    \"continent\": \"Asia\",\n    \"population\": 29136808,\n    \"area\": 147181\n  },\n  {\n    \"countryCode\": \"NL\",\n    \"capital\": \"Amsterdam\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Netherlands\",\n    \"continent\": \"Europe\",\n    \"population\": 16655799,\n    \"area\": 41850\n  },\n  {\n    \"countryCode\": \"NZ\",\n    \"capital\": \"Wellington\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"New Zealand\",\n    \"continent\": \"Oceania\",\n    \"population\": 5084300,\n    \"area\": 270467\n  },\n  {\n    \"countryCode\": \"NI\",\n    \"capital\": \"Managua\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Nicaragua\",\n    \"continent\": \"North America\",\n    \"population\": 6624554,\n    \"area\": 130373\n  },\n  {\n    \"countryCode\": \"NE\",\n    \"capital\": \"Niamey\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Niger\",\n    \"continent\": \"Africa\",\n    \"population\": 24206636,\n    \"area\": 1267000\n  },\n  {\n    \"countryCode\": \"NG\",\n    \"capital\": \"Abuja\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Nigeria\",\n    \"continent\": \"Africa\",\n    \"population\": 206139587,\n    \"area\": 923768\n  },\n  {\n    \"countryCode\": \"MK\",\n    \"capital\": \"Skopje\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"North Macedonia\",\n    \"continent\": \"Europe\",\n    \"population\": 2077132,\n    \"area\": 25713\n  },\n  {\n    \"countryCode\": \"NO\",\n    \"capital\": \"Oslo\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Norway\",\n    \"continent\": \"Europe\",\n    \"population\": 5379475,\n    \"area\": 323802\n  },\n  {\n    \"countryCode\": \"OM\",\n    \"capital\": \"Muscat\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Oman\",\n    \"continent\": \"Asia\",\n    \"population\": 5106622,\n    \"area\": 309500\n  },\n  {\n    \"countryCode\": \"PK\",\n    \"capital\": \"Islamabad\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Pakistan\",\n    \"continent\": \"Asia\",\n    \"population\": 220892331,\n    \"area\": 881912\n  },\n  {\n    \"countryCode\": \"PW\",\n    \"capital\": \"Ngerulmud\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Palau\",\n    \"continent\": \"Oceania\",\n    \"population\": 18092,\n    \"area\": 459\n  },\n  {\n    \"countryCode\": \"PA\",\n    \"capital\": \"Panama City\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Panama\",\n    \"continent\": \"North America\",\n    \"population\": 4314768,\n    \"area\": 75417\n  },\n  {\n    \"countryCode\": \"PG\",\n    \"capital\": \"Port Moresby\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Papua New Guinea\",\n    \"continent\": \"Oceania\",\n    \"population\": 8947027,\n    \"area\": 462840\n  },\n  {\n    \"countryCode\": \"PY\",\n    \"capital\": \"Asunción\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Paraguay\",\n    \"continent\": \"South America\",\n    \"population\": 7132530,\n    \"area\": 406752\n  },\n  {\n    \"countryCode\": \"PE\",\n    \"capital\": \"Lima\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Peru\",\n    \"continent\": \"South America\",\n    \"population\": 32971846,\n    \"area\": 1285216\n  },\n  {\n    \"countryCode\": \"PH\",\n    \"capital\": \"Manila\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Philippines\",\n    \"continent\": \"Asia\",\n    \"population\": 109581085,\n    \"area\": 342353\n  },\n  {\n    \"countryCode\": \"PL\",\n    \"capital\": \"Warsaw\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Poland\",\n    \"continent\": \"Europe\",\n    \"population\": 37950802,\n    \"area\": 312679\n  },\n  {\n    \"countryCode\": \"PT\",\n    \"capital\": \"Lisbon\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Portugal\",\n    \"continent\": \"Europe\",\n    \"population\": 10305564,\n    \"area\": 92090\n  },\n  {\n    \"countryCode\": \"QA\",\n    \"capital\": \"Doha\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Qatar\",\n    \"continent\": \"Asia\",\n    \"population\": 2881060,\n    \"area\": 11586\n  },\n  {\n    \"countryCode\": \"RO\",\n    \"capital\": \"Bucharest\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Romania\",\n    \"continent\": \"Europe\",\n    \"population\": 19286123,\n    \"area\": 238391\n  },\n  {\n    \"countryCode\": \"RU\",\n    \"capital\": \"Moscow\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Russia\",\n    \"continent\": \"Europe\",\n    \"population\": 144104080,\n    \"area\": 17098242\n  },\n  {\n    \"countryCode\": \"RW\",\n    \"capital\": \"Kigali\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Rwanda\",\n    \"continent\": \"Africa\",\n    \"population\": 12952209,\n    \"area\": 26338\n  },\n  {\n    \"countryCode\": \"KN\",\n    \"capital\": \"Basseterre\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Saint Kitts and Nevis\",\n    \"continent\": \"North America\",\n    \"population\": 53192,\n    \"area\": 261\n  },\n  {\n    \"countryCode\": \"LC\",\n    \"capital\": \"Castries\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Saint Lucia\",\n    \"continent\": \"North America\",\n    \"population\": 183629,\n    \"area\": 616\n  },\n  {\n    \"countryCode\": \"VC\",\n    \"capital\": \"Kingstown\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Saint Vincent and the Grenadines\",\n    \"continent\": \"North America\",\n    \"population\": 110947,\n    \"area\": 389\n  },\n  {\n    \"countryCode\": \"WS\",\n    \"capital\": \"Apia\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Samoa\",\n    \"continent\": \"Oceania\",\n    \"population\": 198410,\n    \"area\": 2842\n  },\n  {\n    \"countryCode\": \"SM\",\n    \"capital\": \"San Marino\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"San Marino\",\n    \"continent\": \"Europe\",\n    \"population\": 33938,\n    \"area\": 61\n  },\n  {\n    \"countryCode\": \"ST\",\n    \"capital\": \"São Tomé\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Sao Tome and Principe\",\n    \"continent\": \"Africa\",\n    \"population\": 219161,\n    \"area\": 964\n  },\n  {\n    \"countryCode\": \"SA\",\n    \"capital\": \"Riyadh\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Saudi Arabia\",\n    \"continent\": \"Asia\",\n    \"population\": 34813867,\n    \"area\": 2149690\n  },\n  {\n    \"countryCode\": \"SN\",\n    \"capital\": \"Dakar\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Senegal\",\n    \"continent\": \"Africa\",\n    \"population\": 16743930,\n    \"area\": 196722\n  },\n  {\n    \"countryCode\": \"RS\",\n    \"capital\": \"Belgrade\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Serbia\",\n    \"continent\": \"Europe\",\n    \"population\": 6908224,\n    \"area\": 88361\n  },\n  {\n    \"countryCode\": \"SC\",\n    \"capital\": \"Victoria\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Seychelles\",\n    \"continent\": \"Africa\",\n    \"population\": 98462,\n    \"area\": 452\n  },\n  {\n    \"countryCode\": \"SL\",\n    \"capital\": \"Freetown\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Sierra Leone\",\n    \"continent\": \"Africa\",\n    \"population\": 7976985,\n    \"area\": 71740\n  },\n  {\n    \"countryCode\": \"SG\",\n    \"capital\": \"Singapore\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Singapore\",\n    \"continent\": \"Asia\",\n    \"population\": 5685807,\n    \"area\": 710\n  },\n  {\n    \"countryCode\": \"SK\",\n    \"capital\": \"Bratislava\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Slovakia\",\n    \"continent\": \"Europe\",\n    \"population\": 5458827,\n    \"area\": 49037\n  },\n  {\n    \"countryCode\": \"SI\",\n    \"capital\": \"Ljubljana\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Slovenia\",\n    \"continent\": \"Europe\",\n    \"population\": 2100126,\n    \"area\": 20273\n  },\n  {\n    \"countryCode\": \"SB\",\n    \"capital\": \"Honiara\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Solomon Islands\",\n    \"continent\": \"Oceania\",\n    \"population\": 686878,\n    \"area\": 28896\n  },\n  {\n    \"countryCode\": \"SO\",\n    \"capital\": \"Mogadishu\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Somalia\",\n    \"continent\": \"Africa\",\n    \"population\": 15893219,\n    \"area\": 637657\n  },\n  {\n    \"countryCode\": \"ZA\",\n    \"capital\": \"Pretoria\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"South Africa\",\n    \"continent\": \"Africa\",\n    \"population\": 59308690,\n    \"area\": 1221037\n  },\n  {\n    \"countryCode\": \"SS\",\n    \"capital\": \"Juba\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"South Sudan\",\n    \"continent\": \"Africa\",\n    \"population\": 11193729,\n    \"area\": 619745\n  },\n  {\n    \"countryCode\": \"ES\",\n    \"capital\": \"Madrid\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"Spain\",\n    \"continent\": \"Europe\",\n    \"population\": 47351567,\n    \"area\": 505992\n  },\n  {\n    \"countryCode\": \"LK\",\n    \"capital\": \"Sri Jayawardenepura Kotte\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Sri Lanka\",\n    \"continent\": \"Asia\",\n    \"population\": 21919000,\n    \"area\": 65610\n  },\n  {\n    \"countryCode\": \"SD\",\n    \"capital\": \"Khartoum\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Sudan\",\n    \"continent\": \"Africa\",\n    \"population\": 43849269,\n    \"area\": 1886068\n  },\n  {\n    \"countryCode\": \"SR\",\n    \"capital\": \"Paramaribo\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Suriname\",\n    \"continent\": \"South America\",\n    \"population\": 586634,\n    \"area\": 163820\n  },\n  {\n    \"countryCode\": \"SZ\",\n    \"capital\": \"Mbabane\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Eswatini\",\n    \"continent\": \"Africa\",\n    \"population\": 1160164,\n    \"area\": 17364\n  },\n  {\n    \"countryCode\": \"SE\",\n    \"capital\": \"Stockholm\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Sweden\",\n    \"continent\": \"Europe\",\n    \"population\": 10353442,\n    \"area\": 450295\n  },\n  {\n    \"countryCode\": \"CH\",\n    \"capital\": \"Bern\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Switzerland\",\n    \"continent\": \"Europe\",\n    \"population\": 8654622,\n    \"area\": 41284\n  },\n  {\n    \"countryCode\": \"SY\",\n    \"capital\": \"Damascus\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Syria\",\n    \"continent\": \"Asia\",\n    \"population\": 17500657,\n    \"area\": 185180\n  },\n  {\n    \"countryCode\": \"TW\",\n    \"capital\": \"Taipei\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Taiwan\",\n    \"continent\": \"Asia\",\n    \"population\": 23503349,\n    \"area\": 36193\n  },\n  {\n    \"countryCode\": \"TJ\",\n    \"capital\": \"Dushanbe\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Tajikistan\",\n    \"continent\": \"Asia\",\n    \"population\": 9537642,\n    \"area\": 143100\n  },\n  {\n    \"countryCode\": \"TZ\",\n    \"capital\": \"Dodoma\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Tanzania\",\n    \"continent\": \"Africa\",\n    \"population\": 59734213,\n    \"area\": 945087\n  },\n  {\n    \"countryCode\": \"TH\",\n    \"capital\": \"Bangkok\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Thailand\",\n    \"continent\": \"Asia\",\n    \"population\": 69799978,\n    \"area\": 513120\n  },\n  {\n    \"countryCode\": \"TL\",\n    \"capital\": \"Dili\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Timor-Leste\",\n    \"continent\": \"Asia\",\n    \"population\": 1318442,\n    \"area\": 14874\n  },\n  {\n    \"countryCode\": \"TG\",\n    \"capital\": \"Lomé\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Togo\",\n    \"continent\": \"Africa\",\n    \"population\": 8278737,\n    \"area\": 56785\n  },\n  {\n    \"countryCode\": \"TO\",\n    \"capital\": \"Nuku'alofa\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Tonga\",\n    \"continent\": \"Oceania\",\n    \"population\": 105697,\n    \"area\": 747\n  },\n  {\n    \"countryCode\": \"TT\",\n    \"capital\": \"Port of Spain\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Trinidad and Tobago\",\n    \"continent\": \"North America\",\n    \"population\": 1399491,\n    \"area\": 5130\n  },\n  {\n    \"countryCode\": \"TN\",\n    \"capital\": \"Tunis\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Tunisia\",\n    \"continent\": \"Africa\",\n    \"population\": 11818618,\n    \"area\": 163610\n  },\n  {\n    \"countryCode\": \"TR\",\n    \"capital\": \"Ankara\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Turkey\",\n    \"continent\": \"Asia\",\n    \"population\": 84339067,\n    \"area\": 783562\n  },\n  {\n    \"countryCode\": \"TM\",\n    \"capital\": \"Ashgabat\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Turkmenistan\",\n    \"continent\": \"Asia\",\n    \"population\": 6031187,\n    \"area\": 488100\n  },\n  {\n    \"countryCode\": \"TV\",\n    \"capital\": \"Funafuti\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Tuvalu\",\n    \"continent\": \"Oceania\",\n    \"population\": 11792,\n    \"area\": 26\n  },\n  {\n    \"countryCode\": \"UG\",\n    \"capital\": \"Kampala\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Uganda\",\n    \"continent\": \"Africa\",\n    \"population\": 45741000,\n    \"area\": 241550\n  },\n  {\n    \"countryCode\": \"UA\",\n    \"capital\": \"Kyiv\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Ukraine\",\n    \"continent\": \"Europe\",\n    \"population\": 44134693,\n    \"area\": 603500\n  },\n  {\n    \"countryCode\": \"AE\",\n    \"capital\": \"Abu Dhabi\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"United Arab Emirates\",\n    \"continent\": \"Asia\",\n    \"population\": 9890400,\n    \"area\": 83600\n  },\n  {\n    \"countryCode\": \"GB\",\n    \"capital\": \"London\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"United Kingdom\",\n    \"continent\": \"Europe\",\n    \"population\": 67215293,\n    \"area\": 242900\n  },\n  {\n    \"countryCode\": \"US\",\n    \"capital\": \"Washington D.C.\",\n    \"difficulty\": \"easy\",\n    \"countryName\": \"United States of America\",\n    \"continent\": \"North America\",\n    \"population\": 329484123,\n    \"area\": 9372610\n  },\n  {\n    \"countryCode\": \"UY\",\n    \"capital\": \"Montevideo\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Uruguay\",\n    \"continent\": \"South America\",\n    \"population\": 3473727,\n    \"area\": 181034\n  },\n  {\n    \"countryCode\": \"UZ\",\n    \"capital\": \"Tashkent\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Uzbekistan\",\n    \"continent\": \"Asia\",\n    \"population\": 34232050,\n    \"area\": 447400\n  },\n  {\n    \"countryCode\": \"VU\",\n    \"capital\": \"Port Vila\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Vanuatu\",\n    \"continent\": \"Oceania\",\n    \"population\": 307150,\n    \"area\": 12189\n  },\n  {\n    \"countryCode\": \"VE\",\n    \"capital\": \"Caracas\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Venezuela\",\n    \"continent\": \"South America\",\n    \"population\": 28435943,\n    \"area\": 916445\n  },\n  {\n    \"countryCode\": \"VN\",\n    \"capital\": \"Hanoi\",\n    \"difficulty\": \"medium\",\n    \"countryName\": \"Vietnam\",\n    \"continent\": \"Asia\",\n    \"population\": 97338583,\n    \"area\": 331212\n  },\n  {\n    \"countryCode\": \"EH\",\n    \"capital\": \"El Aaiún\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Western Sahara\",\n    \"continent\": \"Africa\",\n    \"population\": 510713,\n    \"area\": 266000\n  },\n  {\n    \"countryCode\": \"YE\",\n    \"capital\": \"Sana'a\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Yemen\",\n    \"continent\": \"Asia\",\n    \"population\": 29825968,\n    \"area\": 527968\n  },\n  {\n    \"countryCode\": \"ZM\",\n    \"capital\": \"Lusaka\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Zambia\",\n    \"continent\": \"Africa\",\n    \"population\": 18383956,\n    \"area\": 752612\n  },\n  {\n    \"countryCode\": \"ZW\",\n    \"capital\": \"Harare\",\n    \"difficulty\": \"hard\",\n    \"countryName\": \"Zimbabwe\",\n    \"continent\": \"Africa\",\n    \"population\": 14862927,\n    \"area\": 390757\n  }\n]\n"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/local/math_quiz/MathQuizCoreRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.local.math_quiz\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.math.evaluator.Expressions\nimport com.infinitepower.newquiz.data.repository.math_quiz.MathQuizCoreRepositoryImpl\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository\nimport com.infinitepower.newquiz.model.math_quiz.MathFormula\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.Arguments\nimport org.junit.jupiter.params.provider.MethodSource\nimport kotlin.time.measureTime\nimport kotlin.time.measureTimedValue\n\ninternal class MathQuizCoreRepositoryImplTest {\n\n    private lateinit var mathQuizCoreRepository: MathQuizCoreRepositoryImpl\n\n    companion object {\n        @JvmStatic\n        private fun getGenerateMathFormulaParams() = listOf(\n            Arguments.of(1, QuestionDifficulty.Easy),\n            Arguments.of(1, QuestionDifficulty.Medium),\n            Arguments.of(1, QuestionDifficulty.Hard),\n            Arguments.of(2, QuestionDifficulty.Easy),\n            Arguments.of(2, QuestionDifficulty.Medium),\n            Arguments.of(2, QuestionDifficulty.Hard),\n            Arguments.of(3, QuestionDifficulty.Easy),\n            Arguments.of(3, QuestionDifficulty.Medium),\n            Arguments.of(3, QuestionDifficulty.Hard)\n        )\n    }\n\n    @BeforeEach\n    fun setup() {\n        val expressions = Expressions()\n        mathQuizCoreRepository = MathQuizCoreRepositoryImpl(expressions)\n    }\n\n    @ParameterizedTest(name = \"generate math formula, operator size: {0}, difficulty: {1}\")\n    @MethodSource(\"getGenerateMathFormulaParams\")\n    fun `generate math formula`(\n        operatorSize: Int,\n        difficulty: QuestionDifficulty\n    ) {\n        val timedValue = measureTimedValue {\n            mathQuizCoreRepository.generateMathFormula(operatorSize, difficulty)\n        }\n\n        println(\"Time: ${timedValue.duration}\")\n\n        val formula = timedValue.value\n        println(\"Formula: $formula\")\n\n        assertThat(formula.fullFormula).matches(MathFormula.mathFormulaRegex.toPattern())\n        assertThat(formula.solution).isIn(MathQuizCoreRepository.SOLUTION_RANGE)\n\n        val solution = Expressions().eval(formula.leftFormula).toInt()\n        assertThat(solution).isEqualTo(formula.solution)\n    }\n\n    @Test\n    fun `validate correct formulas`() {\n        // Test formulas with not valid operators\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+2s=4\")).isFalse()\n\n        // Test valid formulas\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+2=4\")).isTrue()\n        assertThat(mathQuizCoreRepository.validateFormula(\"2*3=6\")).isTrue()\n        assertThat(mathQuizCoreRepository.validateFormula(\"10/5=2\")).isTrue()\n\n        // Test formulas with multiple equals signs\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+2=4=\")).isFalse()\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+2=4=8\")).isFalse()\n\n        // Test formulas with empty left-hand side expression\n        assertThat(mathQuizCoreRepository.validateFormula(\"=4\")).isFalse()\n        assertThat(mathQuizCoreRepository.validateFormula(\"  =  4  \")).isFalse()\n\n        // Test formulas with empty right-hand side solution\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+2=\")).isFalse()\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+2=  \")).isFalse()\n\n        // Test formulas with invalid right-hand side solution\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+2=abc\")).isFalse()\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+2=1.2.3\")).isFalse()\n\n        // Test formulas with invalid left-hand side expression\n        assertThat(mathQuizCoreRepository.validateFormula(\"2+a=4\")).isFalse()\n\n        measureTime {\n            mathQuizCoreRepository.validateFormula(\"2+2=4\")\n        }.also {\n            println(it)\n        }\n    }\n}"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/local/wordle/WordleCategoriesTest.kt",
    "content": "package com.infinitepower.newquiz.data.local.wordle\n\nimport com.google.common.truth.Truth\nimport org.junit.jupiter.api.Test\n\ninternal class WordleCategoriesTest {\n    @Test\n    fun `test getRandomWordleCategory when not internet connection`() {\n        val allCategories = WordleCategories.allCategories\n        val isInternetAvailable = false\n\n        val categoriesWithoutInternet = allCategories.filter { !it.requireInternetConnection }\n\n        val randomCategory = WordleCategories.random(isInternetAvailable)\n\n        Truth.assertThat(randomCategory).isIn(categoriesWithoutInternet)\n        Truth.assertThat(randomCategory.requireInternetConnection).isEqualTo(isInternetAvailable)\n    }\n\n    @Test\n    fun `test getRandomWordleCategory when internet connection`() {\n        val allCategories = WordleCategories.allCategories\n        val isInternetAvailable = true\n\n        val randomCategory = WordleCategories.random(isInternetAvailable)\n\n        Truth.assertThat(randomCategory).isIn(allCategories)\n\n        // If internet is available, it can return any category\n        Truth.assertThat(randomCategory.requireInternetConnection).isAnyOf(true, false)\n    }\n}"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/comparison_quiz/ComparisonQuizApiImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.comparison_quiz\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.utils.mockAndroidLog\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport com.infinitepower.newquiz.model.country.Continent\nimport com.infinitepower.newquiz.model.country.Country\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.toUiText\nimport io.ktor.client.HttpClient\nimport io.ktor.client.engine.mock.MockEngine\nimport io.ktor.client.engine.mock.respond\nimport io.ktor.http.HttpHeaders\nimport io.ktor.http.HttpStatusCode\nimport io.ktor.http.headersOf\nimport io.ktor.utils.io.ByteReadChannel\nimport io.mockk.coEvery\nimport io.mockk.coVerify\nimport io.mockk.confirmVerified\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport java.net.URI\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [ComparisonQuizApiImpl]\n */\ninternal class ComparisonQuizApiImplTest {\n    private lateinit var comparisonQuizApi: ComparisonQuizApiImpl\n\n    private val countryRepository: CountryRepository = mockk()\n\n    private val mockEngine = MockEngine { request ->\n        val size = request.url.parameters[\"size\"]?.toIntOrNull() ?: 0\n\n        val randomQuestions = List(size) {\n            ComparisonQuizItemEntity(\n                title = \"Item $it\",\n                value = it.toDouble(),\n                imgUrl = \"url$it\"\n            )\n        }\n\n        respond(\n            content = ByteReadChannel(Json.encodeToString(randomQuestions)),\n            status = HttpStatusCode.OK,\n            headers = headersOf(HttpHeaders.ContentType, \"application/json\")\n        )\n    }\n\n    @BeforeTest\n    fun setup() {\n        mockAndroidLog()\n        mockLocalCountries()\n\n        comparisonQuizApi = ComparisonQuizApiImpl(\n            client = HttpClient(mockEngine),\n            countryRepository = countryRepository\n        )\n    }\n\n    @Test\n    fun `generateQuestions should return questions from remote API when generateQuestionsLocally is false`() =\n        runTest {\n            val category = getCategory(generateQuestionsLocally = false)\n            val size = 10\n\n            val questions = comparisonQuizApi.generateQuestions(\n                category = category,\n                size = size\n            )\n\n            assertThat(mockEngine.requestHistory).hasSize(1)\n            assertThat(questions).hasSize(size)\n\n            coVerify(exactly = 0) { countryRepository.getAllCountries() }\n            confirmVerified(countryRepository)\n        }\n\n    @Test\n    fun `generateQuestions should return questions from local repository when generateQuestionsLocally is true and category is supported`() =\n        runTest {\n            val category = getCategory(\n                id = ComparisonQuizApiImpl.supportedLocalCategories.random(),\n                generateQuestionsLocally = true\n            )\n            val size = 10\n\n            val questions = comparisonQuizApi.generateQuestions(\n                category = category,\n                size = size\n            )\n\n            // Check if no calls were made to the remote API\n            assertThat(mockEngine.requestHistory).isEmpty()\n            assertThat(questions).hasSize(size)\n\n            coVerify(exactly = 1) { countryRepository.getAllCountries() }\n            confirmVerified(countryRepository)\n        }\n\n    @Test\n    fun `generateQuestions should return questions from remote API when generateQuestionsLocally is true and category is not supported`() = runTest {\n        val category = getCategory(\n            id = \"not_supported\",\n            generateQuestionsLocally = true\n        )\n        val size = 10\n\n        val questions = comparisonQuizApi.generateQuestions(\n            category = category,\n            size = size\n        )\n\n        assertThat(mockEngine.requestHistory).hasSize(1)\n        assertThat(questions).hasSize(size)\n\n        coVerify(exactly = 0) { countryRepository.getAllCountries() }\n        confirmVerified(countryRepository)\n    }\n\n    private fun getCategory(\n        id: String = \"test\",\n        generateQuestionsLocally: Boolean = false\n    ) = ComparisonQuizCategory(\n        id = id,\n        name = \"test\".toUiText(),\n        image = \"\",\n        description = \"\",\n        questionDescription = ComparisonQuizCategory.QuestionDescription(\n            greater = \"greater\",\n            less = \"less\"\n        ),\n        formatType = NumberFormatType.DEFAULT,\n        generateQuestionsLocally = generateQuestionsLocally\n    )\n\n    private fun mockLocalCountries() {\n        val countries = List(100) {\n            Country(\n                countryCode = \"code$it\",\n                countryName = \"name$it\",\n                capital = \"capital$it\",\n                population = it.toLong(),\n                area = it.toDouble(),\n                continent = Continent.from(\"Europe\"),\n                flagImage = URI.create(\"flag$it\"),\n                difficulty = QuestionDifficulty.random()\n            )\n        }\n        coEvery { countryRepository.getAllCountries() } returns countries\n    }\n}\n"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/comparison_quiz/ComparisonQuizRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.comparison_quiz\n\nimport app.cash.turbine.test\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.database.dao.GameResultDao\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItemEntity\nimport io.mockk.coEvery\nimport io.mockk.coVerify\nimport io.mockk.confirmVerified\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.verify\nimport kotlinx.coroutines.flow.flowOf\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [ComparisonQuizRepositoryImpl]\n */\ninternal class ComparisonQuizRepositoryImplTest {\n    private lateinit var repository: ComparisonQuizRepositoryImpl\n\n    private val remoteConfig: RemoteConfig = mockk()\n    private val gameResultDao: GameResultDao = mockk()\n    private val comparisonQuizApi: ComparisonQuizApi = mockk()\n\n    @BeforeTest\n    fun setUp() {\n        repository = ComparisonQuizRepositoryImpl(\n            remoteConfig = remoteConfig,\n            gameResultDao = gameResultDao,\n            comparisonQuizApi = comparisonQuizApi\n        )\n    }\n\n    @Test\n    fun `getCategories() should return list of categories`() {\n        val categories = List(10) { id ->\n            ComparisonQuizCategory(\n                id = id.toString(),\n                name = UiText.DynamicString(\"name $id\"),\n                image = \"image$id\",\n                description = \"description $id\",\n                formatType = NumberFormatType.DEFAULT,\n                questionDescription = ComparisonQuizCategory.QuestionDescription(\n                    greater = \"greater $id\",\n                    less = \"less $id\"\n                )\n            )\n        }\n\n        val categoriesEntity = categories.map { it.toEntity() }\n\n        every {\n            remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_CATEGORIES)\n        } returns Json.encodeToString(categoriesEntity)\n\n        val result = repository.getCategories()\n\n        assertThat(result).containsExactlyElementsIn(categories)\n\n        verify(exactly = 1) { remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_CATEGORIES) }\n\n        // Check if the second call to getCategories() returns the same list of categories cached\n        // and does not call remoteConfig.get() again\n\n        val result2 = repository.getCategories()\n\n        assertThat(result2).containsExactlyElementsIn(categories)\n\n        // Not called again\n        verify(exactly = 1) { remoteConfig.get(RemoteConfigValue.COMPARISON_QUIZ_CATEGORIES) }\n\n        confirmVerified(remoteConfig)\n    }\n\n    @Test\n    fun `getHighestPosition() should return highest position`() = runTest {\n        val highestPosition = 10\n\n        coEvery {\n            gameResultDao.getComparisonQuizHighestPosition(\"1\")\n        } returns highestPosition\n\n        val result = repository.getHighestPosition(\"1\")\n\n        assertThat(result).isEqualTo(highestPosition)\n\n        coVerify(exactly = 1) { gameResultDao.getComparisonQuizHighestPosition(\"1\") }\n\n        confirmVerified(gameResultDao)\n    }\n\n    @Test\n    fun `getHighestPositionFlow() should return highest position`() = runTest {\n        val highestPosition = 10\n\n        every {\n            gameResultDao.getComparisonQuizHighestPositionFlow(\"1\")\n        } returns flowOf(highestPosition)\n\n        val result = repository.getHighestPositionFlow(\"1\")\n\n        result.test {\n            assertThat(awaitItem()).isEqualTo(highestPosition)\n            awaitComplete()\n        }\n\n        verify(exactly = 1) { gameResultDao.getComparisonQuizHighestPositionFlow(\"1\") }\n\n        confirmVerified(gameResultDao)\n    }\n\n    @Test\n    fun `getQuestions() should return list of questions`() = runTest {\n        val questionsToGenerate = 10\n        val questionsEntity = List(questionsToGenerate) { id ->\n            ComparisonQuizItemEntity(\n                title = \"title $id\",\n                value = id.toDouble(),\n                imgUrl = \"\",\n            )\n        }\n\n        val category = ComparisonQuizCategory(\n            id = \"1\",\n            name = UiText.DynamicString(\"name\"),\n            image = \"image\",\n            description = \"description\",\n            formatType = NumberFormatType.DEFAULT,\n            questionDescription = ComparisonQuizCategory.QuestionDescription(\n                greater = \"greater\",\n                less = \"less\"\n            )\n        )\n\n        coEvery {\n            comparisonQuizApi.generateQuestions(\n                category = category,\n                size = questionsToGenerate,\n            )\n        } returns questionsEntity\n\n        val result = repository.getQuestions(\n            category = category,\n            size = questionsToGenerate,\n        )\n\n        result.forEachIndexed { index, question ->\n            assertThat(question.title).isNotEmpty()\n            assertThat(question.value).isEqualTo(index.toDouble())\n            assertThat(question.imgUri).isNotNull()\n        }\n\n        coVerify(exactly = 1) {\n            comparisonQuizApi.generateQuestions(\n                category = category,\n                size = questionsToGenerate,\n            )\n        }\n\n        confirmVerified(comparisonQuizApi)\n    }\n}\n"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/country/CountryRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.country\n\nimport android.content.Context\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport io.mockk.coEvery\nimport io.mockk.coVerify\nimport io.mockk.confirmVerified\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport kotlin.test.Test\n\n/**\n * Tests for [CountryRepositoryImpl]\n */\ninternal class CountryRepositoryImplTest {\n    @Test\n    fun `test get countries`() = runTest {\n        val context = mockk<Context>()\n\n        val countriesEntities = List(10) {\n            CountryEntity(\n                countryCode = \"TEST_$it\",\n                countryName = \"Test Country $it\",\n                capital = \"Test Capital $it\",\n                continent = \"Europe\",\n                difficulty = \"easy\",\n                population = 1000,\n                area = 1000.0\n            )\n        }\n\n        val inputStream = Json\n            .encodeToString(countriesEntities)\n            .byteInputStream()\n\n        coEvery {\n            context.resources.openRawResource(any())\n        } returns inputStream\n\n        val remoteConfig = mockk<RemoteConfig>()\n        every { remoteConfig.get(RemoteConfigValue.FLAG_BASE_URL) } returns \"https://example.com/%code%.png\"\n\n        val countryRepository = CountryRepositoryImpl(\n            context = context,\n            remoteConfig = remoteConfig\n        )\n\n        val allCountries = countryRepository.getAllCountries()\n\n        assertThat(allCountries).hasSize(countriesEntities.size)\n\n        coVerify(exactly = 1) { context.resources.openRawResource(any()) }\n        coVerify(exactly = 1) { remoteConfig.get(RemoteConfigValue.FLAG_BASE_URL) }\n\n        confirmVerified(context, remoteConfig)\n    }\n}\n"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/country/TestCountryRepositoryImpl.kt",
    "content": "package com.infinitepower.newquiz.data.repository.country\n\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.model.country.Country\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport kotlinx.serialization.json.Json\nimport java.io.File\n\n/**\n * This class is used for loading countries from local json file without needing\n * to use android context. Only used for testing.\n */\ninternal class TestCountryRepositoryImpl : CountryRepository {\n    override suspend fun getAllCountries(): List<Country> {\n        val flagBaseUrl = \"https://example.com/%code%\"\n\n        return getCountryFromJson().map { it.toModel(flagBaseUrl) }\n    }\n\n    private suspend fun getCountryFromJson(): List<CountryEntity> {\n        return withContext(Dispatchers.IO) {\n            val path = \"src/main/res/raw\"\n            val file = File(path, \"all_countries.json\")\n\n            val strRes = file.readText()\n\n            Json.decodeFromString(strRes)\n        }\n    }\n}\n"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/daily_challenge/DailyChallengeRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.daily_challenge\n\nimport app.cash.turbine.test\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.testing.data.fake.FakeComparisonQuizData\nimport com.infinitepower.newquiz.core.testing.data.fake.FakeData\nimport com.infinitepower.newquiz.core.testing.domain.FakeDailyChallengeDao\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.data.util.mappers.daily_challenge.toEntity\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport io.mockk.clearAllMocks\nimport io.mockk.coEvery\nimport io.mockk.coJustRun\nimport io.mockk.coVerify\nimport io.mockk.confirmVerified\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.datetime.Clock\nimport org.junit.jupiter.api.assertThrows\nimport kotlin.test.AfterTest\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\nimport kotlin.time.Duration.Companion.days\n\ninternal class DailyChallengeRepositoryImplTest {\n    private val dailyChallengeDao = FakeDailyChallengeDao()\n    private val comparisonQuizRepository = mockk<ComparisonQuizRepository>()\n    private val remoteConfig = mockk<RemoteConfig>()\n    private val userService = mockk<UserService>()\n\n    private lateinit var dailyChallengeRepository: DailyChallengeRepositoryImpl\n\n    @BeforeTest\n    fun setup() {\n        val comparisonQuizCategories = FakeComparisonQuizData.generateCategories()\n        coEvery { comparisonQuizRepository.getCategories() } returns comparisonQuizCategories\n\n        dailyChallengeRepository = DailyChallengeRepositoryImpl(\n            dailyChallengeDao = dailyChallengeDao,\n            comparisonQuizRepository = comparisonQuizRepository,\n            remoteConfig = remoteConfig,\n            userService = userService\n        )\n    }\n\n    @AfterTest\n    fun tearDown() {\n        clearAllMocks()\n        runTest {\n            dailyChallengeDao.deleteAll()\n        }\n    }\n\n    @Test\n    fun `getAllTasks returns all tasks`() = runTest {\n        val tasks = FakeData.generateTasks()\n        dailyChallengeDao.insertAll(tasks.map(DailyChallengeTask::toEntity))\n\n        val resultTasks = dailyChallengeRepository.getAllTasks()\n\n        assertThat(resultTasks).containsExactlyElementsIn(tasks)\n\n        coVerify(exactly = 1) { comparisonQuizRepository.getCategories() }\n        confirmVerified()\n    }\n\n    @Test\n    fun `getAvailableTasks returns available tasks`() = runTest {\n        val tasks = FakeData.generateTasks()\n        val expiredTasks = FakeData.generateTasks(\n            count = 5,\n            dayDuration = (-2).days..(-1).days\n        )\n        val newTasks = FakeData.generateTasks(\n            count = 5,\n            dayDuration = (1).days..10.days\n        )\n        val allTasks = tasks + expiredTasks + newTasks\n        dailyChallengeDao.insertAll(allTasks.map(DailyChallengeTask::toEntity))\n\n        val resultTasks = dailyChallengeRepository.getAvailableTasks()\n\n        assertThat(resultTasks).containsExactlyElementsIn(tasks)\n\n        coVerify(exactly = 1) { comparisonQuizRepository.getCategories() }\n        confirmVerified()\n    }\n\n    @Test\n    fun `getAvailableTasksFlow returns available tasks`() = runTest {\n        val tasks = FakeData.generateTasks()\n        val expiredTasks = FakeData.generateTasks(\n            count = 5,\n            dayDuration = (-2).days..(-1).days\n        )\n        val newTasks = FakeData.generateTasks(\n            count = 5,\n            dayDuration = (1).days..10.days\n        )\n        val allTasks = tasks + expiredTasks + newTasks\n\n        dailyChallengeRepository.getAvailableTasksFlow().test {\n            dailyChallengeDao.insertAll(allTasks.map(DailyChallengeTask::toEntity))\n\n            assertThat(awaitItem()).isEmpty()\n            assertThat(awaitItem()).containsExactlyElementsIn(tasks)\n        }\n\n        coVerify(exactly = 1) { comparisonQuizRepository.getCategories() }\n        confirmVerified()\n    }\n\n    @Test\n    fun `getClaimableTasksCountFlow returns correct count`() = runTest {\n        val tasks = FakeData.generateTasks().toMutableList().apply {\n            set(0, get(0).copy(currentValue = 10u, maxValue = 10u))\n        }\n\n        dailyChallengeRepository.getClaimableTasksCountFlow().test {\n            dailyChallengeDao.insertAll(tasks.map(DailyChallengeTask::toEntity))\n\n            assertThat(awaitItem()).isEqualTo(0)\n            assertThat(awaitItem()).isEqualTo(tasks.count { it.isClaimable() })\n        }\n\n        coVerify(exactly = 1) { comparisonQuizRepository.getCategories() }\n        confirmVerified()\n    }\n\n    @Test\n    fun `resetTasks deletes all tasks`() = runTest {\n        val tasks = FakeData.generateTasks()\n        dailyChallengeDao.insertAll(tasks.map(DailyChallengeTask::toEntity))\n\n        dailyChallengeRepository.resetTasks()\n\n        confirmVerified() // No calls should be made to the mock\n\n        // Verify that the tasks are deleted\n        assertThat(dailyChallengeRepository.getAllTasks()).isEmpty()\n    }\n\n    @Test\n    fun `checkAndGenerateTasksIfNeeded does not generate tasks if they are not expired`() =\n        runTest {\n            val tasks = FakeData.generateTasks()\n            dailyChallengeDao.insertAll(tasks.map(DailyChallengeTask::toEntity))\n\n            dailyChallengeRepository.checkAndGenerateTasksIfNeeded(tasksToGenerate = 10)\n\n            coVerify(exactly = 0) {\n                comparisonQuizRepository.getCategories()\n                remoteConfig.get(RemoteConfigValue.DAILY_CHALLENGE_ITEM_REWARD)\n            }\n            confirmVerified()\n\n            // Verify that the tasks are not changed\n            assertThat(dailyChallengeRepository.getAllTasks()).containsExactlyElementsIn(tasks)\n        }\n\n    @Test\n    fun `checkAndGenerateTasksIfNeeded generates tasks if they are expired`() = runTest {\n        val tasksToGenerate = 10\n        val diamondsReward = 10\n\n        val expiredTasks = FakeData.generateTasks(\n            count = 5,\n            dayDuration = (-2).days..(-1).days // 2 days ago\n        )\n        dailyChallengeDao.insertAll(expiredTasks.map(DailyChallengeTask::toEntity))\n\n        every { remoteConfig.get(RemoteConfigValue.DAILY_CHALLENGE_ITEM_REWARD) } returns diamondsReward\n\n        dailyChallengeRepository.checkAndGenerateTasksIfNeeded(tasksToGenerate = tasksToGenerate)\n\n        coVerify(exactly = 1) {\n            comparisonQuizRepository.getCategories()\n            remoteConfig.get(RemoteConfigValue.DAILY_CHALLENGE_ITEM_REWARD)\n        }\n        confirmVerified()\n\n        // Check if the tasks are generated\n        dailyChallengeRepository\n            .getAvailableTasks()\n            .also {\n                assertThat(it).hasSize(tasksToGenerate)\n            }.forEach {\n                assertThat(it.isClaimed).isFalse()\n                assertThat(it.currentValue).isEqualTo(0u)\n                assertThat(it.isClaimed).isFalse()\n\n                val now = Clock.System.now()\n                assertThat(now).isAtLeast(it.dateRange.start)\n                assertThat(now).isAtMost(it.dateRange.endInclusive)\n            }\n    }\n\n    @Test\n    fun `completeTaskStep throws exception if task is not found`() = runTest {\n        assertThrows<NullPointerException> {\n            dailyChallengeRepository.completeTaskStep(GameEvent.MultiChoice.PlayQuestions)\n        }\n    }\n\n    @Test\n    fun `completeTaskStep throws exception if task is expired`() = runTest {\n        val tasks = FakeData.generateTasks(\n            count = 5,\n            dayDuration = (-2).days..(-1).days\n        )\n        dailyChallengeDao.insertAll(tasks.map(DailyChallengeTask::toEntity))\n\n        assertThrows<IllegalStateException> {\n            dailyChallengeRepository.completeTaskStep(tasks.first().event)\n        }\n    }\n\n    @Test\n    fun `completeTaskStep throws exception if task is already claimed`() = runTest {\n        val now = Clock.System.now()\n        val dateRange = now..now + 1.days\n        val task = FakeData\n            .generateTask(id = 1, dateRange = dateRange)\n            .copy(isClaimed = true)\n\n        dailyChallengeDao.insertAll(task.toEntity())\n\n        assertThrows<IllegalStateException> {\n            dailyChallengeRepository.completeTaskStep(task.event)\n        }\n    }\n\n    @Test\n    fun `completeTaskStep updates the current value if exists and is available`() = runTest {\n        val now = Clock.System.now()\n        val dateRange = now..now + 1.days\n        val task = FakeData.generateTask(id = 1, dateRange = dateRange)\n\n        assertThat(task.currentValue).isEqualTo(0u)\n\n        dailyChallengeDao.insertAll(task.toEntity())\n\n        dailyChallengeRepository.completeTaskStep(task.event)\n\n        val updatedTask = dailyChallengeDao.getTaskByType(task.event.key)!!\n        assertThat(updatedTask.currentValue).isEqualTo(1)\n    }\n\n    @Test\n    fun `claimTask throws exception if task is not found`() = runTest {\n        assertThrows<NullPointerException> {\n            dailyChallengeRepository.claimTask(GameEvent.MultiChoice.PlayQuestions)\n        }\n    }\n\n    @Test\n    fun `claimTask throws exception if task is expired`() = runTest {\n        val tasks = FakeData.generateTasks(\n            count = 5,\n            dayDuration = (-2).days..(-1).days\n        )\n        dailyChallengeDao.insertAll(tasks.map(DailyChallengeTask::toEntity))\n\n        assertThrows<IllegalStateException> {\n            dailyChallengeRepository.claimTask(tasks.first().event)\n        }\n    }\n\n    @Test\n    fun `claimTask throws exception if task is already claimed`() = runTest {\n        val now = Clock.System.now()\n        val dateRange = now..now + 1.days\n        val task = FakeData\n            .generateTask(id = 1, dateRange = dateRange)\n            .copy(isClaimed = true)\n\n        dailyChallengeDao.insertAll(task.toEntity())\n\n        assertThrows<IllegalStateException> {\n            dailyChallengeRepository.claimTask(task.event)\n        }\n    }\n\n    @Test\n    fun `claimTask throws exception if task is not completed`() = runTest {\n        val now = Clock.System.now()\n        val dateRange = now..now + 1.days\n        val task = FakeData.generateTask(id = 1, dateRange = dateRange)\n            .copy(currentValue = 5u, maxValue = 10u)\n\n        dailyChallengeDao.insertAll(task.toEntity())\n\n        assertThrows<IllegalStateException> {\n            dailyChallengeRepository.claimTask(task.event)\n        }\n        confirmVerified()\n    }\n\n    @Test\n    fun `claimTask updates the task if exists and is available`() = runTest {\n        val now = Clock.System.now()\n        val dateRange = now..now + 1.days\n        val task = FakeData\n            .generateTask(id = 1, dateRange = dateRange)\n            .copy(currentValue = 10u, maxValue = 10u)\n\n        assertThat(task.isClaimable()).isTrue()\n        assertThat(task.isClaimed).isFalse()\n\n        coJustRun { userService.addRemoveDiamonds(task.diamondsReward.toInt()) }\n\n        dailyChallengeDao.insertAll(task.toEntity())\n\n        dailyChallengeRepository.claimTask(task.event)\n\n        val updatedTask = dailyChallengeDao.getTaskByType(task.event.key)!!\n        assertThat(updatedTask.isClaimed).isTrue()\n\n        coVerify(exactly = 1) {\n            userService.addRemoveDiamonds(task.diamondsReward.toInt())\n        }\n        confirmVerified()\n    }\n}\n"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/home/RecentCategoriesRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.home\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport com.infinitepower.newquiz.core.datastore.common.RecentCategoryDataStoreCommon\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.local.wordle.WordleCategories\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.toUiText\nimport io.mockk.clearAllMocks\nimport io.mockk.coEvery\nimport io.mockk.coJustRun\nimport io.mockk.coVerify\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.slot\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.flow.flowOf\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.Arguments\nimport org.junit.jupiter.params.provider.MethodSource\n\n/**\n * Tests for [RecentCategoriesRepositoryImpl]\n */\ninternal class RecentCategoriesRepositoryImplTest {\n    private lateinit var recentCategoriesRepository: RecentCategoriesRepositoryImpl\n\n    private val recentCategoriesDataStoreManager: DataStoreManager = mockk(relaxed = true)\n    private val settingsDataStoreManager: DataStoreManager = mockk(relaxed = true)\n    private val comparisonQuizRepository: ComparisonQuizRepository = mockk(relaxed = true)\n    private val remoteConfig: RemoteConfig = mockk(relaxed = true)\n\n    @BeforeEach\n    fun setUp() {\n        every { settingsDataStoreManager.getPreferenceFlow(SettingsCommon.HideOnlineCategories) } returns flowOf(false)\n\n        // Create an instance of RecentCategoriesRepositoryImpl with mocked dependencies\n        recentCategoriesRepository = RecentCategoriesRepositoryImpl(\n            recentCategoriesDataStoreManager,\n            settingsDataStoreManager,\n            comparisonQuizRepository,\n            remoteConfig\n        )\n    }\n\n    @AfterEach\n    fun tearDown() {\n        // Clear any recorded calls and reset mocks after each test\n        clearAllMocks()\n    }\n\n    private data class TestCategory(\n        override val gameMode: GameMode,\n        override val id: String,\n        override val name: UiText,\n        override val image: String,\n        override val requireInternetConnection: Boolean\n    ) : BaseCategory\n\n    private val testCategories = List(10) {\n        TestCategory(\n            gameMode = GameMode.MULTI_CHOICE,\n            id = \"id$it\",\n            name = \"name$it\".toUiText(),\n            image = \"image$it\",\n            requireInternetConnection = it % 2 == 0\n        )\n    }\n\n    @Test\n    fun `getCategories should return expected result, when connection available and have recent categories`() = runTest {\n        // Mock the necessary dependencies and data\n        val recentCategories = listOf(\n            testCategories[0],\n            testCategories[1],\n            testCategories[2]\n        )\n\n        val recentCategoriesIds = recentCategories\n            .map { it.id }\n            .toSet()\n\n        // Call the method under test\n        val result = recentCategoriesRepository.getHomeCategories(\n            allCategories = testCategories,\n            recentCategoriesFlow = flowOf(recentCategoriesIds),\n            hideOnlineCategoriesFlow = flowOf(false),\n            isInternetAvailable = true\n        ).first()\n\n        val otherCategories = testCategories.filterNot { it in recentCategories }\n\n        // Verify the result\n        assertThat(result.recentCategories.size).isAtMost(3)\n        assertThat(result.recentCategories).containsExactlyElementsIn(recentCategories)\n\n        assertThat(result.otherCategories).containsExactlyElementsIn(otherCategories)\n    }\n\n    @Test\n    fun `getCategories should return expected result, when connection available and have no recent categories`() = runTest {\n        // Mock the necessary dependencies and data\n        val recentCategoriesIds = emptySet<String>()\n\n        // Call the method under test\n        val result = recentCategoriesRepository.getHomeCategories(\n            allCategories = testCategories,\n            recentCategoriesFlow = flowOf(recentCategoriesIds),\n            hideOnlineCategoriesFlow = flowOf(false),\n            isInternetAvailable = true\n        ).first()\n\n        // Verify the result\n        assertThat(result.recentCategories).isNotEmpty()\n        assertThat(result.recentCategories).hasSize(3)\n\n        assertThat(result.otherCategories).hasSize(testCategories.size - 3)\n        assertThat(result.otherCategories).containsExactlyElementsIn(testCategories - result.recentCategories.toSet())\n    }\n\n    @Test\n    fun `getCategories should return expected result, when connection not available and have recent categories`() = runTest {\n        // Mock the necessary dependencies and data\n        val recentCategories = listOf(\n            testCategories[0],\n            testCategories[1],\n            testCategories[2]\n        )\n\n        val recentCategoriesIds = recentCategories\n            .map { it.id }\n            .toSet()\n\n        // Call the method under test\n        val result = recentCategoriesRepository.getHomeCategories(\n            allCategories = testCategories,\n            recentCategoriesFlow = flowOf(recentCategoriesIds),\n            hideOnlineCategoriesFlow = flowOf(false),\n            isInternetAvailable = false\n        ).first()\n\n        val otherCategories = testCategories.filterNot { it in recentCategories }\n\n        // Verify the result\n        assertThat(result.recentCategories.size).isAtMost(3)\n        assertThat(result.recentCategories).containsExactlyElementsIn(recentCategories)\n\n        assertThat(result.otherCategories).containsExactlyElementsIn(otherCategories)\n\n        // Check that the other categories with no internet connection are on the top of the list\n        val otherCategoriesSorted = otherCategories.sortedBy { it.requireInternetConnection }\n        assertThat(result.otherCategories).isEqualTo(otherCategoriesSorted)\n    }\n\n    @Test\n    fun `getCategories should return expected result, when connection not available and have no recent categories`() = runTest {\n        // Mock the necessary dependencies and data\n        val recentCategoriesIds = emptySet<String>()\n\n        // Call the method under test\n        val result = recentCategoriesRepository.getHomeCategories(\n            allCategories = testCategories,\n            recentCategoriesFlow = flowOf(recentCategoriesIds),\n            hideOnlineCategoriesFlow = flowOf(false),\n            isInternetAvailable = false\n        ).first()\n\n        // Verify the result\n        assertThat(result.recentCategories).isNotEmpty()\n\n        assertThat(result.recentCategories.size).isAtMost(3)\n\n        assertThat(result.otherCategories).hasSize(testCategories.size - result.recentCategories.size)\n        assertThat(result.otherCategories).containsExactlyElementsIn(testCategories - result.recentCategories.toSet())\n    }\n\n    @Test\n    fun `getCategories should return expected result, when connection not available and have no recent categories and all categories require internet`() = runTest {\n        val testCategories = List(10) {\n            TestCategory(\n                gameMode = GameMode.MULTI_CHOICE,\n                id = \"id$it\",\n                name = \"name$it\".toUiText(),\n                image = \"image$it\",\n                requireInternetConnection = true\n            )\n        }\n\n        // Mock the necessary dependencies and data\n        val recentCategoriesIds = emptySet<String>()\n\n        // Call the method under test\n        val result = recentCategoriesRepository.getHomeCategories(\n            allCategories = testCategories,\n            recentCategoriesFlow = flowOf(recentCategoriesIds),\n            hideOnlineCategoriesFlow = flowOf(false),\n            isInternetAvailable = false\n        ).first()\n\n        // Verify the result\n        assertThat(result.recentCategories).isNotEmpty()\n        assertThat(result.recentCategories).hasSize(3)\n\n        assertThat(result.otherCategories).hasSize(testCategories.size - result.recentCategories.size)\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"getMultiChoiceCategoriesParams\")\n    fun `getMultiChoiceCategories should return expected result with parameters`(\n        isInternetAvailable: Boolean,\n        recentCategoriesIds: Set<String>\n    ) = runTest {\n        every { recentCategoriesDataStoreManager.getPreferenceFlow<Set<String>>(any()) } returns flowOf(recentCategoriesIds)\n\n        // Call the method under test\n        val result = recentCategoriesRepository.getMultiChoiceCategories(\n            isInternetAvailable = isInternetAvailable\n        ).first()\n\n        // Verify the result\n        assertThat(result).isNotNull()\n\n        assertThat(result.recentCategories).isNotEmpty()\n        assertThat(result.recentCategories.size).isAtMost(3)\n\n        if (recentCategoriesIds.isNotEmpty()) {\n            val recentCategories = multiChoiceQuestionCategories.filter { it.id in recentCategoriesIds }\n            assertThat(result.recentCategories).containsExactlyElementsIn(recentCategories)\n        }\n\n        assertThat(result.otherCategories).hasSize(multiChoiceQuestionCategories.size - result.recentCategories.size)\n        assertThat(result.otherCategories).containsExactlyElementsIn(multiChoiceQuestionCategories - result.recentCategories.toSet())\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"getWordleCategoriesParams\")\n    fun `getWordCategories should return expected result with parameters`(\n        isInternetAvailable: Boolean,\n        recentCategoriesIds: Set<String>\n    ) = runTest {\n        every { recentCategoriesDataStoreManager.getPreferenceFlow<Set<String>>(any()) } returns flowOf(recentCategoriesIds)\n\n        // Call the method under test\n        val result = recentCategoriesRepository.getWordleCategories(\n            isInternetAvailable = isInternetAvailable\n        ).first()\n\n        // Verify the result\n        assertThat(result).isNotNull()\n\n        assertThat(result.recentCategories).isNotEmpty()\n        assertThat(result.recentCategories.size).isAtMost(3)\n\n        val allWordleCategories = WordleCategories.allCategories\n\n        if (recentCategoriesIds.isNotEmpty()) {\n            val recentCategories = allWordleCategories.filter { it.id in recentCategoriesIds }\n            assertThat(result.recentCategories).containsExactlyElementsIn(recentCategories)\n        }\n\n        assertThat(result.otherCategories).hasSize(allWordleCategories.size - result.recentCategories.size)\n        assertThat(result.otherCategories).containsExactlyElementsIn(allWordleCategories - result.recentCategories.toSet())\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"getComparisonCategoriesParams\")\n    fun `getComparisonCategories should return expected result with parameters`(\n        isInternetAvailable: Boolean,\n        recentCategoriesIds: Set<String>\n    ) = runTest {\n        every { comparisonQuizRepository.getCategories() } returns allComparisonQuizCategories\n        every { recentCategoriesDataStoreManager.getPreferenceFlow<Set<String>>(any()) } returns flowOf(recentCategoriesIds)\n\n        // Call the method under test\n        val result = recentCategoriesRepository.getComparisonCategories(\n            isInternetAvailable = isInternetAvailable\n        ).first()\n\n        // Verify the result\n        assertThat(result).isNotNull()\n\n        assertThat(result.recentCategories).isNotEmpty()\n        assertThat(result.recentCategories.size).isAtMost(3)\n\n        if (recentCategoriesIds.isNotEmpty()) {\n            val recentCategories = allComparisonQuizCategories.filter { it.id in recentCategoriesIds }\n            assertThat(result.recentCategories).containsExactlyElementsIn(recentCategories)\n        }\n\n        assertThat(result.otherCategories).hasSize(allComparisonQuizCategories.size - result.recentCategories.size)\n        assertThat(result.otherCategories).containsExactlyElementsIn(allComparisonQuizCategories - result.recentCategories.toSet())\n    }\n\n    // Tests for addMultiChoiceCategory\n    @ParameterizedTest\n    @MethodSource(\"addMultiChoiceCategoryParams\")\n    fun `addMultiChoiceCategory should add category to recent categories`(\n        categoryIdToAdd: String,\n        initialCategories: Set<String>\n    ) = runTest {\n        // Mock the necessary dependencies and data\n        coEvery { recentCategoriesDataStoreManager.getPreference<Set<String>>(any()) } returns initialCategories\n        coJustRun { recentCategoriesDataStoreManager.editPreference<Set<String>>(any(), any()) }\n\n        // Act\n        recentCategoriesRepository.addMultiChoiceCategory(categoryIdToAdd)\n\n        // Assert\n        val slot = slot<PreferenceRequest<Set<String>>>()\n        coVerify { recentCategoriesDataStoreManager.getPreference(capture(slot)) }\n        assertThat(slot.captured.key).isEqualTo(RecentCategoryDataStoreCommon.MultiChoice.key)\n\n        val addFunctionShouldBeCalled = categoryIdToAdd !in initialCategories\n\n        val newValueSlot = slot<Set<String>>()\n        coVerify(\n            exactly = if (addFunctionShouldBeCalled) 1 else 0\n        ) { recentCategoriesDataStoreManager.editPreference(any(), capture(newValueSlot)) }\n\n        if (addFunctionShouldBeCalled) {\n            assertThat(newValueSlot.captured.size).isAtMost(3)\n            assertThat(newValueSlot.captured).contains(categoryIdToAdd)\n        } else {\n            assertThat(newValueSlot.isCaptured).isFalse()\n        }\n    }\n\n    // Tests for addWordleCategory\n    @ParameterizedTest\n    @MethodSource(\"addWordleCategoryParams\")\n    fun `addWordleCategory should add category to recent categories`(\n        categoryIdToAdd: String,\n        initialCategories: Set<String>\n    ) = runTest {\n        // Mock the necessary dependencies and data\n        coEvery { recentCategoriesDataStoreManager.getPreference<Set<String>>(any()) } returns initialCategories\n        coJustRun { recentCategoriesDataStoreManager.editPreference<Set<String>>(any(), any()) }\n\n        // Act\n        recentCategoriesRepository.addWordleCategory(categoryIdToAdd)\n\n        // Assert\n        val slot = slot<PreferenceRequest<Set<String>>>()\n        coVerify { recentCategoriesDataStoreManager.getPreference(capture(slot)) }\n        assertThat(slot.captured.key).isEqualTo(RecentCategoryDataStoreCommon.Wordle.key)\n\n        val addFunctionShouldBeCalled = categoryIdToAdd !in initialCategories\n\n        val newValueSlot = slot<Set<String>>()\n        coVerify(\n            exactly = if (addFunctionShouldBeCalled) 1 else 0\n        ) { recentCategoriesDataStoreManager.editPreference(any(), capture(newValueSlot)) }\n\n        if (addFunctionShouldBeCalled) {\n            assertThat(newValueSlot.captured.size).isAtMost(3)\n            assertThat(newValueSlot.captured).contains(categoryIdToAdd)\n        } else {\n            assertThat(newValueSlot.isCaptured).isFalse()\n        }\n    }\n\n    // Tests for addComparisonCategory\n    @ParameterizedTest\n    @MethodSource(\"addComparisonCategoryParams\")\n    fun `addComparisonCategory should add category to recent categories`(\n        categoryIdToAdd: String,\n        initialCategories: Set<String>\n    ) = runTest {\n        // Mock the necessary dependencies and data\n        coEvery { recentCategoriesDataStoreManager.getPreference<Set<String>>(any()) } returns initialCategories\n        coJustRun { recentCategoriesDataStoreManager.editPreference<Set<String>>(any(), any()) }\n\n        // Act\n        recentCategoriesRepository.addComparisonCategory(categoryIdToAdd)\n\n        // Assert\n        val slot = slot<PreferenceRequest<Set<String>>>()\n        coVerify { recentCategoriesDataStoreManager.getPreference(capture(slot)) }\n        assertThat(slot.captured.key).isEqualTo(RecentCategoryDataStoreCommon.ComparisonQuiz.key)\n\n        val addFunctionShouldBeCalled = categoryIdToAdd !in initialCategories\n\n        val newValueSlot = slot<Set<String>>()\n        coVerify(\n            exactly = if (addFunctionShouldBeCalled) 1 else 0\n        ) { recentCategoriesDataStoreManager.editPreference(any(), capture(newValueSlot)) }\n\n        if (addFunctionShouldBeCalled) {\n            assertThat(newValueSlot.captured.size).isAtMost(3)\n            assertThat(newValueSlot.captured).contains(categoryIdToAdd)\n        } else {\n            assertThat(newValueSlot.isCaptured).isFalse()\n        }\n    }\n\n    // Tests for cleanAllSavedCategories\n    @Test\n    fun `cleanAllSavedCategories should clean all saved categories`() = runTest {\n        // Mock the necessary dependencies and data\n        coJustRun { recentCategoriesDataStoreManager.editPreference<Set<String>>(any(), any()) }\n\n        // Act\n        recentCategoriesRepository.cleanAllSavedCategories()\n\n        coVerify(exactly = 1) {\n            recentCategoriesDataStoreManager.editPreference(\n                key = RecentCategoryDataStoreCommon.MultiChoice.key,\n                newValue = emptySet()\n            )\n            recentCategoriesDataStoreManager.editPreference(\n                key = RecentCategoryDataStoreCommon.Wordle.key,\n                newValue = emptySet()\n            )\n            recentCategoriesDataStoreManager.editPreference(\n                key = RecentCategoryDataStoreCommon.ComparisonQuiz.key,\n                newValue = emptySet()\n            )\n        }\n    }\n\n    companion object {\n        // Params for getCategories tests\n        @JvmStatic\n        private fun getCategoriesParams(\n            allCategories: List<BaseCategory>\n        ) = listOf(\n            Arguments.of(\n                true,\n                setOf(\n                    allCategories[0].id,\n                    allCategories[1].id,\n                    allCategories[2].id\n                )\n            ),\n            Arguments.of(\n                false,\n                setOf(\n                    allCategories[0].id,\n                    allCategories[1].id,\n                    allCategories[2].id\n                )\n            ),\n            Arguments.of(\n                true,\n                emptySet<String>()\n            ),\n            Arguments.of(\n                false,\n                emptySet<String>()\n            ),\n            Arguments.of(\n                true,\n                setOf(\n                    allCategories.first().id\n                )\n            )\n        )\n\n        @JvmStatic\n        fun getMultiChoiceCategoriesParams() = getCategoriesParams(multiChoiceQuestionCategories)\n\n        @JvmStatic\n        fun getWordleCategoriesParams() = getCategoriesParams(WordleCategories.allCategories)\n\n        @JvmStatic\n        fun getComparisonCategoriesParams() = getCategoriesParams(allComparisonQuizCategories)\n\n        // Params for addCategory tests\n        @JvmStatic\n        private fun addCategoryParams(\n            allCategories: List<BaseCategory>\n        ) = listOf(\n            Arguments.of(\n                allCategories[0].id,\n                emptySet<BaseCategory>()\n            ),\n            Arguments.of(\n                allCategories[0].id,\n                setOf(\n                    allCategories[1].id\n                )\n            ),\n            Arguments.of(\n                allCategories[0].id,\n                setOf(\n                    allCategories[1].id,\n                    allCategories[2].id\n                )\n            ),\n            Arguments.of(\n                allCategories[0].id,\n                setOf(\n                    allCategories[1].id,\n                    allCategories[2].id,\n                    allCategories[3].id\n                )\n            ),\n            // Test that adding a category that already exists in the set will not change the set\n            Arguments.of(\n                allCategories[0].id,\n                setOf(\n                    allCategories[0].id,\n                )\n            ),\n            // Test that adding a category that already exists in the set will not change the set\n            Arguments.of(\n                allCategories[1].id,\n                setOf(\n                    allCategories[0].id,\n                    allCategories[1].id\n                )\n            ),\n            // Test that adding a category that already exists in the set will not change the set\n            Arguments.of(\n                allCategories[1].id,\n                setOf(\n                    allCategories[0].id,\n                    allCategories[1].id,\n                    allCategories[2].id\n                )\n            ),\n        )\n\n        @JvmStatic\n        fun addMultiChoiceCategoryParams() = addCategoryParams(multiChoiceQuestionCategories)\n\n        @JvmStatic\n        fun addWordleCategoryParams() = addCategoryParams(WordleCategories.allCategories)\n\n        @JvmStatic\n        fun addComparisonCategoryParams() = addCategoryParams(allComparisonQuizCategories)\n\n        private val allComparisonQuizCategories = listOf(\n            ComparisonQuizCategory(\n                id = \"numbers\",\n                name = \"Numbers\".toUiText(),\n                description = \"Numbers description\",\n                image = \"\",\n                questionDescription = ComparisonQuizCategory.QuestionDescription(\n                    greater = \"Which number is greater?\",\n                    less = \"Which number is lesser?\",\n                ),\n                formatType = NumberFormatType.DEFAULT,\n                dataSourceAttribution = ComparisonQuizCategory.DataSourceAttribution(\n                    text = \"NewQuiz API\",\n                    logo = \"\"\n                ),\n                requireInternetConnection = false\n            ),\n            ComparisonQuizCategory(\n                id = \"countries\",\n                name = \"Countries\".toUiText(),\n                description = \"Countries description\",\n                image = \"\",\n                questionDescription = ComparisonQuizCategory.QuestionDescription(\n                    greater = \"Which country is bigger?\",\n                    less = \"Which country is smaller?\",\n                ),\n                formatType = NumberFormatType.DEFAULT,\n                dataSourceAttribution = ComparisonQuizCategory.DataSourceAttribution(\n                    text = \"NewQuiz API\",\n                    logo = \"\"\n                ),\n                requireInternetConnection = false\n            ),\n            ComparisonQuizCategory(\n                id = \"cities\",\n                name = \"Cities\".toUiText(),\n                description = \"Cities description\",\n                image = \"\",\n                questionDescription = ComparisonQuizCategory.QuestionDescription(\n                    greater = \"Which city is bigger?\",\n                    less = \"Which city is smaller?\",\n                ),\n                formatType = NumberFormatType.DEFAULT,\n                dataSourceAttribution = ComparisonQuizCategory.DataSourceAttribution(\n                    text = \"NewQuiz API\",\n                    logo = \"\"\n                ),\n                requireInternetConnection = true\n            ),\n            ComparisonQuizCategory(\n                id = \"planets\",\n                name = \"Planets\".toUiText(),\n                description = \"Planets description\",\n                image = \"\",\n                questionDescription = ComparisonQuizCategory.QuestionDescription(\n                    greater = \"Which planet is bigger?\",\n                    less = \"Which planet is smaller?\",\n                ),\n                formatType = NumberFormatType.DEFAULT,\n                dataSourceAttribution = ComparisonQuizCategory.DataSourceAttribution(\n                    text = \"NewQuiz API\",\n                    logo = \"\"\n                ),\n                requireInternetConnection = true\n            ),\n        )\n    }\n}\n"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/maze_quiz/MazeQuizRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.maze_quiz\n\nimport app.cash.turbine.test\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.analytics.LocalDebugAnalyticsHelper\nimport com.infinitepower.newquiz.core.database.dao.MazeQuizDao\nimport com.infinitepower.newquiz.data.util.mappers.maze.toEntity\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport io.mockk.coEvery\nimport io.mockk.coJustRun\nimport io.mockk.coVerify\nimport io.mockk.confirmVerified\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlinx.coroutines.flow.flowOf\nimport kotlinx.coroutines.test.runTest\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\ninternal class MazeQuizRepositoryImplTest {\n    private val mazeQuizDao = mockk<MazeQuizDao>()\n\n    private lateinit var mazeQuizRepository: MazeQuizRepository\n\n    @BeforeTest\n    fun setup() {\n        mazeQuizRepository = MazeQuizRepositoryImpl(\n            mazeQuizDao = mazeQuizDao,\n            analyticsHelper = LocalDebugAnalyticsHelper()\n        )\n    }\n\n    @Test\n    fun `get saved maze quiz flow test`() = runTest {\n        val items = createItems()\n        every {\n            mazeQuizDao.getAllMazeItemsFlow()\n        } returns flowOf(items.map(MazeQuiz.MazeItem::toEntity))\n\n        mazeQuizRepository.getSavedMazeQuizFlow().test {\n            assertThat(awaitItem().items).hasSize(10)\n            awaitComplete()\n        }\n    }\n\n    @Test\n    fun `completeMazeItem should complete maze item`() = runTest {\n        val items = createItems()\n        val firstItem = items.first()\n\n        coEvery { mazeQuizDao.getAllMazeItems() } returns items.map(MazeQuiz.MazeItem::toEntity)\n        coJustRun { mazeQuizDao.updateItem(firstItem.toEntity().copy(played = true)) }\n\n        mazeQuizRepository.completeMazeItem(firstItem.id)\n\n        coVerify(exactly = 1) {\n            mazeQuizDao.getAllMazeItems()\n            mazeQuizDao.updateItem(firstItem.toEntity().copy(played = true))\n        }\n        confirmVerified()\n    }\n\n    private fun createItems(count: Int = 10): List<MazeQuiz.MazeItem> {\n        return List(count) {\n            MazeQuiz.MazeItem.Wordle(\n                difficulty = QuestionDifficulty.Easy,\n                wordleWord = WordleWord(\"1+1=2\"),\n                wordleQuizType = WordleQuizType.MATH_FORMULA,\n                mazeSeed = 0\n            )\n        }\n    }\n}"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/CountryCapitalFlagsQuizRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.data.repository.country.TestCountryRepositoryImpl\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ValueSource\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [CountryCapitalFlagsQuizRepositoryImpl]\n */\ninternal class CountryCapitalFlagsQuizRepositoryImplTest {\n    private val countryRepository: CountryRepository = TestCountryRepositoryImpl()\n\n    private lateinit var repository: CountryCapitalFlagsQuizRepositoryImpl\n\n    @BeforeTest\n    fun setUp() {\n        repository = CountryCapitalFlagsQuizRepositoryImpl(\n            countryRepository = countryRepository\n        )\n    }\n\n    @Test\n    fun `getFlagQuiz() returns a list of questions`() = runTest {\n        val questionSize = 5\n\n        val questions = repository.getRandomQuestions(\n            amount = questionSize,\n            category = MultiChoiceBaseCategory.CountryCapitalFlags\n        )\n\n        assertThat(questions).hasSize(questionSize)\n\n        // Use imageUrl because it is unique for each question\n        val uniqueQuestions = questions.distinctBy { it.image }\n        assertThat(uniqueQuestions).hasSize(questionSize)\n\n        // Check if the questions category is correct\n        assertThat(questions.all { it.category == MultiChoiceBaseCategory.CountryCapitalFlags }).isTrue()\n\n        val allCountries = countryRepository.getAllCountries()\n\n        // Check if the answer capital corresponds to the country\n        questions.forEach { question ->\n            val capitalAnswer = question.answers[question.correctAns]\n\n            val countryName = question\n                .description\n                .removePrefix(\"What is the capital of \")\n                .removeSuffix(\"?\")\n\n            val country = allCountries.find { it.countryName == countryName }\n            assertThat(country).isNotNull()\n            require(country != null)\n\n            assertThat(capitalAnswer).isEqualTo(country.capital)\n        }\n    }\n\n    @ParameterizedTest(name = \"getRandomQuestions returns questions filtered by difficulty: {0}\")\n    @ValueSource(strings = [\"easy\", \"medium\", \"hard\"])\n    fun `getRandomQuestions() returns questions filtered by difficulty`(\n        difficulty: String\n    ) = runTest {\n        val questionSize = 5\n\n        val questions = repository.getRandomQuestions(\n            amount = questionSize,\n            category = MultiChoiceBaseCategory.CountryCapitalFlags,\n            difficulty = difficulty\n        )\n\n        assertThat(questions).hasSize(questionSize)\n\n        // Check if the questions difficulty is correct\n        assertThat(questions.all { it.difficulty == QuestionDifficulty.from(difficulty) }).isTrue()\n    }\n}\n"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/multi_choice_quiz/FlagQuizRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.multi_choice_quiz\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.data.repository.country.TestCountryRepositoryImpl\nimport com.infinitepower.newquiz.domain.repository.CountryRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ValueSource\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [FlagQuizRepositoryImpl]\n */\ninternal class FlagQuizRepositoryImplTest {\n    private val countryRepository: CountryRepository = TestCountryRepositoryImpl()\n\n    private lateinit var repository: FlagQuizRepositoryImpl\n\n    @BeforeTest\n    fun setUp() {\n        repository = FlagQuizRepositoryImpl(\n            countryRepository = countryRepository\n        )\n    }\n\n    @Test\n    fun `getRandomQuestions() returns a list of questions`() = runTest {\n        val questionSize = 5\n\n        val questions = repository.getRandomQuestions(\n            amount = questionSize,\n            category = MultiChoiceBaseCategory.Flag\n        )\n\n        assertThat(questions).hasSize(questionSize)\n\n        // Use imageUrl because it is unique for each question\n        val uniqueQuestions = questions.distinctBy { it.image }\n        assertThat(uniqueQuestions).hasSize(questionSize)\n\n        // Check if the questions category is correct\n        assertThat(questions.all { it.category == MultiChoiceBaseCategory.Flag }).isTrue()\n    }\n\n    @ParameterizedTest(name = \"getRandomQuestions returns questions filtered by difficulty: {0}\")\n    @ValueSource(strings = [\"easy\", \"medium\", \"hard\"])\n    fun `getRandomQuestions() returns questions filtered by difficulty`(\n        difficulty: String\n    ) = runTest {\n        val questionSize = 5\n\n        val questions = repository.getRandomQuestions(\n            amount = questionSize,\n            category = MultiChoiceBaseCategory.Flag,\n            difficulty = difficulty\n        )\n\n        assertThat(questions).hasSize(questionSize)\n\n        // Check if the questions difficulty is correct\n        assertThat(questions.all { it.difficulty == QuestionDifficulty.from(difficulty) }).isTrue()\n    }\n}"
  },
  {
    "path": "data/src/test/java/com/infinitepower/newquiz/data/repository/wordle/WordleRepositoryImplTest.kt",
    "content": "package com.infinitepower.newquiz.data.repository.wordle\n\nimport android.content.Context\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.domain.repository.math_quiz.MathQuizCoreRepository\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionRepository\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport io.mockk.clearAllMocks\nimport io.mockk.coEvery\nimport io.mockk.coVerify\nimport io.mockk.confirmVerified\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.CsvSource\nimport kotlin.test.AfterTest\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [WordleRepositoryImpl]\n */\ninternal class WordleRepositoryImplTest {\n    private lateinit var repository: WordleRepositoryImpl\n\n    private val context = mockk<Context>(relaxed = true)\n    private val settingsDataStoreManager = mockk<DataStoreManager>()\n    private val mathQuizCoreRepository = mockk<MathQuizCoreRepository>()\n    private val numberTriviaQuestionRepository = mockk<NumberTriviaQuestionRepository>()\n\n    @BeforeTest\n    fun setup() {\n        repository = WordleRepositoryImpl(\n            context = context,\n            settingsDataStoreManager = settingsDataStoreManager,\n            mathQuizCoreRepository = mathQuizCoreRepository,\n            numberTriviaQuestionRepository = numberTriviaQuestionRepository\n        )\n    }\n\n    @AfterTest\n    fun tearDown() {\n        clearAllMocks()\n    }\n\n    @Test\n    fun `getAllWords should return a list of words`() = runTest {\n        coEvery {\n            settingsDataStoreManager.getPreference(SettingsCommon.InfiniteWordleQuizLanguage)\n        } returns \"en\"\n        mockkWordsResources()\n\n        val words = repository.getAllWords()\n        assertThat(words).isNotEmpty()\n        assertThat(words).hasSize(3)\n\n        coVerify(exactly = 1) {\n            settingsDataStoreManager.getPreference(SettingsCommon.InfiniteWordleQuizLanguage)\n            context.resources.openRawResource(any())\n        }\n        confirmVerified()\n    }\n\n    @Test\n    fun `getAllWords should throw an exception when language is not supported`() = runTest {\n        coEvery {\n            settingsDataStoreManager.getPreference(SettingsCommon.InfiniteWordleQuizLanguage)\n        } returns \"a\"\n        mockkWordsResources()\n\n        assertThrows<NullPointerException> {\n            repository.getAllWords()\n        }\n\n        coVerify(exactly = 1) {\n            settingsDataStoreManager.getPreference(SettingsCommon.InfiniteWordleQuizLanguage)\n        }\n        confirmVerified()\n    }\n\n    @Test\n    fun `generateRandomTextWord should return a random word`() = runTest {\n        coEvery {\n            settingsDataStoreManager.getPreference(SettingsCommon.InfiniteWordleQuizLanguage)\n        } returns \"en\"\n        mockkWordsResources()\n\n        val word = repository.generateRandomTextWord()\n\n        coVerify(exactly = 1) {\n            settingsDataStoreManager.getPreference(SettingsCommon.InfiniteWordleQuizLanguage)\n            context.resources.openRawResource(any())\n        }\n        confirmVerified()\n\n        assertThat(word.word).isNotEmpty()\n        assertThat(allWords).contains(word.word.lowercase())\n    }\n\n    @CsvSource(\n        \"word,TEXT\",\n        \"123,NUMBER\",\n        \"123,NUMBER_TRIVIA\",\n        \"1+2=3,MATH_FORMULA\"\n    )\n    @ParameterizedTest\n    fun `validateWord should return true when word is valid`(\n        word: String,\n        quizType: WordleQuizType\n    ) = runTest {\n        every { mathQuizCoreRepository.validateFormula(any()) } returns true\n        val result = repository.validateWord(word, quizType)\n\n        assertThat(result.isSuccess).isTrue()\n    }\n\n    private val allWords = listOf(\"word1\", \"word2\", \"word3\", \"wordignored\")\n\n    private fun mockkWordsResources() {\n        val words = allWords.joinToString(\"\\n\")\n        every { context.resources.openRawResource(any()) } returns words.byteInputStream()\n    }\n}\n"
  },
  {
    "path": "detekt-compose.yml",
    "content": "Compose:\n  ComposableAnnotationNaming:\n    active: true\n  ComposableNaming:\n    active: true\n  ComposableParamOrder:\n    active: false # Modifiers are order first\n  CompositionLocalAllowlist:\n    active: true\n    allowedCompositionLocals: LocalAnalyticsHelper\n  CompositionLocalNaming:\n    active: true\n  ContentEmitterReturningValues:\n    active: true\n  ContentTrailingLambda:\n    active: true\n  DefaultsVisibility:\n    active: true\n  LambdaParameterInRestartableEffect:\n    active: true\n  Material2:\n    active: false\n  ModifierClickableOrder:\n    active: true\n  ModifierComposable:\n    active: true\n  ModifierComposed:\n    active: true\n  ModifierMissing:\n    active: true\n  ModifierNaming:\n    active: true\n  ModifierNotUsedAtRoot:\n    active: true\n  ModifierReused:\n    active: true\n  ModifierWithoutDefault:\n    active: true\n  MultipleEmitters:\n    active: true\n  MutableParams:\n    active: true\n  MutableStateAutoboxing:\n    active: true\n  MutableStateParam:\n    active: true\n  ParameterNaming:\n    active: true\n  PreviewAnnotationNaming:\n    active: true\n  PreviewPublic:\n    active: true\n  RememberMissing:\n    active: true\n  RememberContentMissing:\n    active: true\n  UnstableCollections:\n    active: false\n  ViewModelForwarding:\n    active: true\n  ViewModelInjection:\n    active: true\n"
  },
  {
    "path": "detekt.yml",
    "content": "config:\n  warningsAsErrors: true\n\ncomplexity:\n  active: true\n  LongParameterList:\n    # It is suggested to increase this for Compose: https://detekt.dev/docs/introduction/compose/#longparameterlist\n    functionThreshold: 8\n    constructorThreshold: 20\n    ignoreDefaultParameters: true\n    ignoreAnnotated:\n      - \"Composable\"\n  LongMethod:\n    active: true\n    excludes: [ \"*/generated/**\" ]\n    threshold: 100\n    ignoreAnnotated:\n      - \"Composable\"\n  CyclomaticComplexMethod:\n    active: true\n    threshold: 15\n    ignoreAnnotated:\n      - \"Composable\"\n  TooManyFunctions:\n    active: true\n    thresholdInClasses: 20\n    ignoreAnnotated:\n      - \"InstallIn\"\n      - \"TestInstallIn\"\n\nnaming:\n  active: true\n  FunctionNaming:\n    active: true\n    functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)'\n    excludeClassPattern: '$^'\n    ignoreAnnotated: [ 'Composable' ]\n  TopLevelPropertyNaming:\n    active: true\n    constantPattern: '[A-Za-z][_A-Za-z0-9]*'\n    propertyPattern: '[A-Za-z][_A-Za-z0-9]*'\n    privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'\n  PackageNaming:\n    active: true\n    packagePattern: '[a-z]+(\\.[a-z][A-Za-z0-9_]*)*'\n\nstyle:\n  MagicNumber:\n    ignoreAnnotated: [ 'Preview', 'PreviewScreenSizes', 'PreviewFontScale', 'PreviewLightDark', 'PreviewDynamicColors' ]\n    excludes: [ '**/test/**', '**/*Test.kt', '**/demos/**', '**/androidUnitTest/**', '**/commonTest/**', '**/desktopTest/**' , '**/jsTest/**' ]\n  MaxLineLength:\n    excludes: [ '**/test/**', '**/*.Test.kt', '**/*.Spec.kt', '**/androidUnitTest/**', '**/commonTest/**', '**/desktopTest/**' , '**/jsTest/**' ]\n    ignoreAnnotated:\n      - \"InstallIn\"\n      - \"TestInstallIn\"\n    excludeCommentStatements: true\n  UnusedPrivateMember:\n    # https://detekt.dev/docs/introduction/compose#unusedprivatemember\n    ignoreAnnotated: [ 'Preview', 'PreviewScreenSizes', 'PreviewFontScale', 'PreviewLightDark', 'PreviewDynamicColors' ]\n  WildcardImport:\n    active: true\n    excludeImports: [ ]\n  ReturnCount:\n    active: true\n    max: 3\n    ignoreAnnotated: [ 'Composable' ]\n\nexceptions:\n  # TODO: Add this back when exceptions are caught correctly\n  TooGenericExceptionCaught:\n    active: false\n"
  },
  {
    "path": "domain/.gitignore",
    "content": "/build"
  },
  {
    "path": "domain/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.jvm.library)\n    alias(libs.plugins.newquiz.detekt)\n}\n\ndependencies {\n    implementation(projects.model)\n\n    api(libs.kotlinx.collections.immutable)\n\n    implementation(libs.javax.inject)\n    implementation(libs.androidx.annotation)\n    implementation(libs.kotlinx.coroutines.core)\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/CountryRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository\n\nimport com.infinitepower.newquiz.model.country.Country\n\ninterface CountryRepository {\n    suspend fun getAllCountries(): List<Country>\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/UserConfigRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository\n\nimport com.infinitepower.newquiz.model.regional_preferences.RegionalPreferences\n\ninterface UserConfigRepository {\n    suspend fun getRegionalPreferences(): RegionalPreferences\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/comparison_quiz/ComparisonQuizRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.comparison_quiz\n\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizItem\nimport kotlinx.coroutines.flow.Flow\nimport kotlin.random.Random\n\ninterface ComparisonQuizRepository {\n    fun getCategories(): List<ComparisonQuizCategory>\n\n    fun getCategoryById(id: String): ComparisonQuizCategory?\n\n    suspend fun getQuestions(\n        category: ComparisonQuizCategory,\n        size: Int = 30,\n        random: Random = Random\n    ): List<ComparisonQuizItem>\n\n    suspend fun getHighestPosition(\n        categoryId: String\n    ): Int\n\n    /**\n     * Get the highest position of the [category].\n     */\n    fun getHighestPositionFlow(\n        categoryId: String\n    ): Flow<Int>\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/daily_challenge/DailyChallengeRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.daily_challenge\n\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport kotlinx.coroutines.flow.Flow\nimport kotlin.random.Random\n\n/**\n * Daily challenge repository interface\n */\ninterface DailyChallengeRepository {\n    /**\n     * Returns all the daily challenge tasks\n     */\n    fun getAvailableTasksFlow(): Flow<List<DailyChallengeTask>>\n\n    suspend fun getAllTasks(): List<DailyChallengeTask>\n\n    suspend fun getAvailableTasks(): List<DailyChallengeTask>\n\n    fun getClaimableTasksCountFlow(): Flow<Int>\n\n    /**\n     * Checks if the daily tasks are expired and generates new ones if needed.\n     * Will be generated unique types of tasks.\n     *\n     * @param tasksToGenerate The number of tasks to generate\n     */\n    suspend fun checkAndGenerateTasksIfNeeded(\n        tasksToGenerate: Int = 5,\n        random: Random = Random\n    )\n\n    /**\n     * Completes the task step if it exists.\n     * If the task is not found, nothing happens.\n     */\n    suspend fun completeTaskStep(taskType: GameEvent)\n\n    suspend fun claimTask(taskType: GameEvent)\n\n    suspend fun resetTasks()\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/home/RecentCategoriesRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.home\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceCategory\nimport com.infinitepower.newquiz.model.wordle.WordleCategory\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\nimport kotlinx.coroutines.flow.Flow\n\n@Keep\ndata class HomeCategories <T> (\n    val recentCategories: ImmutableList<T>,\n    val otherCategories: ImmutableList<T>\n)\n\nfun <T> emptyHomeCategories() = HomeCategories<T>(\n    recentCategories = persistentListOf(),\n    otherCategories = persistentListOf()\n)\n\ntypealias HomeCategoriesFlow <T> = Flow<HomeCategories<T>>\n\ninterface RecentCategoriesRepository {\n    fun getMultiChoiceCategories(\n        isInternetAvailable: Boolean\n    ): HomeCategoriesFlow<MultiChoiceCategory>\n\n    fun getWordleCategories(\n        isInternetAvailable: Boolean\n    ): HomeCategoriesFlow<WordleCategory>\n\n    fun getComparisonCategories(\n        isInternetAvailable: Boolean\n    ): HomeCategoriesFlow<ComparisonQuizCategory>\n\n    fun getDefaultShowCategoryConnectionInfo(): ShowCategoryConnectionInfo\n\n    fun getShowCategoryConnectionInfoFlow(): Flow<ShowCategoryConnectionInfo>\n\n    suspend fun addMultiChoiceCategory(categoryId: String)\n\n    suspend fun addWordleCategory(categoryId: String)\n\n    suspend fun addComparisonCategory(categoryId: String)\n\n    suspend fun cleanAllSavedCategories()\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/math_quiz/MathQuizCoreRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.math_quiz\n\nimport com.infinitepower.newquiz.model.math_quiz.MathFormula\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport kotlin.random.Random\n\ninterface MathQuizCoreRepository {\n    companion object {\n        /**\n         * The range of numbers that can be generated for the solution of the formula.\n         */\n        val SOLUTION_RANGE = -999..999\n\n        const val MAX_FORMULA_LENGTH = 10\n\n        /**\n         * The range of numbers that can be generated for each number of the formula.\n         */\n        val QuestionDifficulty.numbers\n            get() = when (this) {\n                QuestionDifficulty.Easy -> 0..9\n                QuestionDifficulty.Medium -> 0..49\n                QuestionDifficulty.Hard -> 0..99\n            }\n\n        /**\n         * The operators that can be generated for each difficulty.\n         */\n        val QuestionDifficulty.operators\n            get() = when (this) {\n                QuestionDifficulty.Easy -> listOf('+', '-')\n                QuestionDifficulty.Medium -> listOf('+', '-', '*')\n                QuestionDifficulty.Hard -> listOf('+', '-', '*', '/')\n            }\n\n        val QuestionDifficulty.operatorSizeRange\n            get() = when (this) {\n                QuestionDifficulty.Easy -> 1..1\n                QuestionDifficulty.Medium -> 1..2\n                QuestionDifficulty.Hard -> 1..3\n            }\n    }\n\n    fun generateMathFormula(\n        operatorSize: Int = 1,\n        difficulty: QuestionDifficulty = QuestionDifficulty.Easy,\n        random: Random = Random\n    ): MathFormula\n\n    fun validateFormula(formula: String): Boolean\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/maze/MazeQuizRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.maze\n\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport kotlinx.coroutines.flow.Flow\n\ninterface MazeQuizRepository {\n    fun getSavedMazeQuizFlow(): Flow<MazeQuiz>\n\n    suspend fun countAllItems(): Int\n\n    suspend fun insertItems(items: List<MazeQuiz.MazeItem>)\n\n    suspend fun removeItems(items: List<MazeQuiz.MazeItem>)\n\n    suspend fun getMazeItemById(id: Int): MazeQuiz.MazeItem?\n\n    suspend fun getNextAvailableMazeItem(): MazeQuiz.MazeItem?\n\n    suspend fun completeMazeItem(id: Int)\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/multi_choice_quiz/CountryCapitalFlagsQuizRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\n\ninterface CountryCapitalFlagsQuizRepository :\n    MultiChoiceQuestionBaseRepository<MultiChoiceBaseCategory.CountryCapitalFlags>\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/multi_choice_quiz/FlagQuizRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\n\ninterface FlagQuizRepository : MultiChoiceQuestionBaseRepository<MultiChoiceBaseCategory.Flag>\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/multi_choice_quiz/GuessMathSolutionRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\n\ninterface GuessMathSolutionRepository : MultiChoiceQuestionBaseRepository<MultiChoiceBaseCategory.GuessMathSolution>\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/multi_choice_quiz/LogoQuizRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\n\ninterface LogoQuizRepository : MultiChoiceQuestionBaseRepository<MultiChoiceBaseCategory.Logo>\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/multi_choice_quiz/MultiChoiceQuestionBaseRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport kotlin.random.Random\n\nsealed interface MultiChoiceQuestionBaseRepository <T : MultiChoiceBaseCategory> {\n    suspend fun getRandomQuestions(\n        amount: Int = 5,\n        category: T,\n        difficulty: String? = null,\n        random: Random = Random\n    ): List<MultiChoiceQuestion>\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/multi_choice_quiz/MultiChoiceQuestionRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.multi_choice_quiz\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\n\ninterface MultiChoiceQuestionRepository : MultiChoiceQuestionBaseRepository<MultiChoiceBaseCategory.Normal>\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/multi_choice_quiz/saved_questions/SavedMultiChoiceQuestionsRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.saved.SortSavedQuestionsBy\nimport kotlinx.coroutines.flow.Flow\n\ninterface SavedMultiChoiceQuestionsRepository {\n    suspend fun insertQuestions(questions: List<MultiChoiceQuestion>)\n\n    suspend fun insertQuestions(vararg questions: MultiChoiceQuestion)\n\n    fun getFlowQuestions(\n        sortBy: SortSavedQuestionsBy = SortSavedQuestionsBy.BY_DEFAULT\n    ): Flow<List<MultiChoiceQuestion>>\n\n    suspend fun getQuestions(): List<MultiChoiceQuestion>\n\n    fun getCount(): Flow<Int>\n\n    suspend fun deleteAllSelected(questions: List<MultiChoiceQuestion>)\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/numbers/NumberTriviaQuestionApi.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.numbers\n\nimport androidx.annotation.IntRange\nimport com.infinitepower.newquiz.model.number.NumberTriviaQuestionsEntity\n\ninterface NumberTriviaQuestionApi {\n    suspend fun getRandomQuestion(\n        size: Int,\n        @IntRange(from = 0) minNumber: Int,\n        @IntRange(from = 0) maxNumber: Int\n    ): NumberTriviaQuestionsEntity\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/numbers/NumberTriviaQuestionRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.numbers\n\nimport androidx.annotation.IntRange\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.number.NumberTriviaQuestion\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport kotlin.random.Random\n\ninterface NumberTriviaQuestionRepository {\n    suspend fun generateRandomQuestions(\n        size: Int = 1,\n        @IntRange(from = 0) minNumber: Int,\n        @IntRange(from = 0) maxNumber: Int,\n        random: Random = Random\n    ): List<NumberTriviaQuestion>\n\n    suspend fun generateWordleQuestion(\n        random: Random = Random\n    ): WordleWord\n\n    suspend fun generateMultiChoiceQuestion(\n        size: Int = 1,\n        random: Random = Random\n    ): List<MultiChoiceQuestion>\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/repository/wordle/WordleRepository.kt",
    "content": "package com.infinitepower.newquiz.domain.repository.wordle\n\nimport com.infinitepower.newquiz.model.FlowResource\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport kotlin.random.Random\n\ninterface WordleRepository {\n    suspend fun getAllWords(): Set<String>\n\n    fun generateRandomWord(\n        quizType: WordleQuizType,\n        random: Random = Random\n    ): FlowResource<WordleWord>\n\n    suspend fun generateRandomTextWord(random: Random = Random): WordleWord\n\n    suspend fun generateRandomTextWords(\n        count: Int = 5,\n        random: Random = Random\n    ): List<WordleWord>\n\n    suspend fun generateRandomNumberWord(\n        wordSize: Int = 5,\n        random: Random = Random\n    ): WordleWord\n\n    suspend fun isColorBlindEnabled(): Boolean\n\n    suspend fun isLetterHintEnabled(): Boolean\n\n    suspend fun isHardModeEnabled(): Boolean\n\n    suspend fun getWordleMaxRows(\n        defaultMaxRow: Int? = null\n    ): Int\n\n    fun validateWord(word: String, quizType: WordleQuizType): Result<Unit>\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/use_case/question/GetRandomMultiChoiceQuestionUseCase.kt",
    "content": "package com.infinitepower.newquiz.domain.use_case.question\n\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.CountryCapitalFlagsQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.FlagQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.GuessMathSolutionRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.LogoQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.MultiChoiceQuestionRepository\nimport com.infinitepower.newquiz.domain.repository.numbers.NumberTriviaQuestionRepository\nimport com.infinitepower.newquiz.model.FlowResource\nimport com.infinitepower.newquiz.model.Resource\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport kotlinx.coroutines.flow.flow\nimport javax.inject.Inject\n\nclass GetRandomMultiChoiceQuestionUseCase @Inject constructor(\n    private val normalQuestionRepository: MultiChoiceQuestionRepository,\n    private val flagQuizRepository: FlagQuizRepository,\n    private val logoQuizRepository: LogoQuizRepository,\n    private val guessMathSolutionRepository: GuessMathSolutionRepository,\n    private val numberTriviaQuestionRepository: NumberTriviaQuestionRepository,\n    private val countryCapitalFlagsQuizRepository: CountryCapitalFlagsQuizRepository\n) {\n    operator fun invoke(\n        amount: Int = 5,\n        category: MultiChoiceBaseCategory? = MultiChoiceBaseCategory.Normal(),\n        difficulty: String? = null\n    ): FlowResource<List<MultiChoiceQuestion>> = flow {\n        try {\n            emit(Resource.Loading())\n\n            val questions = when (category) {\n                is MultiChoiceBaseCategory.Normal -> normalQuestionRepository.getRandomQuestions(\n                    amount,\n                    category,\n                    difficulty\n                )\n\n                is MultiChoiceBaseCategory.Flag -> flagQuizRepository.getRandomQuestions(\n                    amount,\n                    category,\n                    difficulty\n                )\n\n                is MultiChoiceBaseCategory.Logo -> logoQuizRepository.getRandomQuestions(\n                    amount,\n                    category,\n                    difficulty\n                )\n\n                is MultiChoiceBaseCategory.GuessMathSolution -> guessMathSolutionRepository.getRandomQuestions(\n                    amount,\n                    category,\n                    difficulty\n                )\n\n                is MultiChoiceBaseCategory.CountryCapitalFlags -> countryCapitalFlagsQuizRepository.getRandomQuestions(\n                    amount,\n                    category,\n                    difficulty\n                )\n\n                is MultiChoiceBaseCategory.NumberTrivia -> numberTriviaQuestionRepository.generateMultiChoiceQuestion(\n                    size = amount\n                )\n\n                null -> throw IllegalArgumentException(\"Quiz type is null\")\n            }\n\n            emit(Resource.Success(questions))\n        } catch (e: Exception) {\n            e.printStackTrace()\n            emit(Resource.Error(e.localizedMessage ?: \"Error while loading questions\"))\n        }\n    }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/infinitepower/newquiz/domain/use_case/question/IsQuestionSavedUseCase.kt",
    "content": "package com.infinitepower.newquiz.domain.use_case.question\n\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport com.infinitepower.newquiz.model.FlowResource\nimport com.infinitepower.newquiz.model.Resource\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport kotlinx.coroutines.flow.emitAll\nimport kotlinx.coroutines.flow.flow\nimport kotlinx.coroutines.flow.map\nimport javax.inject.Inject\n\n/**\n * Use case to check if a question is locally saved.\n */\nclass IsQuestionSavedUseCase @Inject constructor(\n    private val savedQuestionRepository: SavedMultiChoiceQuestionsRepository\n) {\n    operator fun invoke(question: MultiChoiceQuestion): FlowResource<Boolean> = flow {\n        try {\n            emit(Resource.Loading())\n\n            // Get all the saved questions\n            val savedQuestionsFlow = savedQuestionRepository.getFlowQuestions()\n\n            savedQuestionsFlow.map { savedQuestions ->\n                // Check if the question exists in the saved questions\n                val questionExists = savedQuestions.any { savedQuestion ->\n                    savedQuestion == question\n                }\n\n                Resource.Success(questionExists)\n            }.also { questionExistsFlow -> emitAll(questionExistsFlow) }\n        } catch (e: Exception) {\n            e.printStackTrace()\n            emit(Resource.Error(e.localizedMessage ?: \"An unknown error occurred...\"))\n        }\n    }\n}\n"
  },
  {
    "path": "domain/src/test/kotlin/com/infinitepower/newquiz/domain/use_case/question/IsQuestionSavedUseCaseTest.kt",
    "content": "package com.infinitepower.newquiz.domain.use_case.question\n\nimport app.cash.turbine.test\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport com.infinitepower.newquiz.model.Resource\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport io.mockk.every\nimport io.mockk.mockk\nimport kotlinx.coroutines.flow.flowOf\nimport kotlinx.coroutines.test.runTest\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [IsQuestionSavedUseCase].\n */\ninternal class IsQuestionSavedUseCaseTest {\n    private val savedQuestionRepository: SavedMultiChoiceQuestionsRepository = mockk()\n\n    private lateinit var isQuestionSavedUseCase: IsQuestionSavedUseCase\n\n    @BeforeTest\n    fun setUp() {\n        isQuestionSavedUseCase = IsQuestionSavedUseCase(savedQuestionRepository)\n    }\n\n    @Test\n    fun `should return Resource#Success(true) when question is saved`() = runTest {\n        val questions = List(5) { getBasicMultiChoiceQuestion(it) }\n\n        every { savedQuestionRepository.getFlowQuestions() } returns flowOf(questions)\n\n        isQuestionSavedUseCase(questions.first()).test {\n            assertThat(awaitItem()).isEqualTo(Resource.Loading(null))\n            assertThat(awaitItem()).isEqualTo(Resource.Success(true))\n            awaitComplete()\n        }\n    }\n\n    @Test\n    fun `should return Resource#Success(false) when question is not saved`() = runTest {\n        val questions = List(5) { getBasicMultiChoiceQuestion(it) }\n\n        every { savedQuestionRepository.getFlowQuestions() } returns flowOf(questions)\n\n        isQuestionSavedUseCase(getBasicMultiChoiceQuestion(-1)).test {\n            assertThat(awaitItem()).isEqualTo(Resource.Loading(null))\n            assertThat(awaitItem()).isEqualTo(Resource.Success(false))\n            awaitComplete()\n        }\n    }\n\n    @Test\n    fun `should return Resource#Success(false) when questions saved are empty`() = runTest {\n        every { savedQuestionRepository.getFlowQuestions() } returns flowOf(emptyList())\n\n        isQuestionSavedUseCase(getBasicMultiChoiceQuestion(-1)).test {\n            assertThat(awaitItem()).isEqualTo(Resource.Loading(null))\n            assertThat(awaitItem()).isEqualTo(Resource.Success(false))\n            awaitComplete()\n        }\n    }\n\n    @Test\n    fun `should return Resource#Error when an exception occurs`() = runTest {\n        val exception = Exception(\"An error occurred...\")\n\n        every { savedQuestionRepository.getFlowQuestions() } throws exception\n\n        isQuestionSavedUseCase(getBasicMultiChoiceQuestion(-1)).test {\n            assertThat(awaitItem()).isEqualTo(Resource.Loading(null))\n            assertThat(awaitItem()).isEqualTo(Resource.Error(exception.localizedMessage, null))\n            awaitComplete()\n        }\n    }\n}\n"
  },
  {
    "path": "feature/daily-challenge/.gitignore",
    "content": "/build"
  },
  {
    "path": "feature/daily-challenge/README.md",
    "content": "# :feature:daily-challenge module\n"
  },
  {
    "path": "feature/daily-challenge/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.feature)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.feature.daily_challenge\"\n}\n\ndependencies {\n    implementation(libs.kotlinx.datetime)\n\n    implementation(projects.core.userServices)\n    implementation(projects.domain)\n    implementation(projects.data)\n}\n"
  },
  {
    "path": "feature/daily-challenge/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />\n"
  },
  {
    "path": "feature/daily-challenge/src/main/kotlin/com/infinitepower/newquiz/feature/daily_challenge/DailyChallengeScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.daily_challenge\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.produceState\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.LocalAnalyticsHelper\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.core.util.plus\nimport com.infinitepower.newquiz.feature.daily_challenge.components.DailyChallengeCard\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.coroutines.delay\nimport kotlinx.datetime.Clock\nimport kotlin.time.Duration.Companion.minutes\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@Destination\n@OptIn(ExperimentalMaterial3Api::class)\nfun DailyChallengeScreen(\n    destinationsNavigator: DestinationsNavigator,\n    dailyChallengeScreenNavigator: DailyChallengeScreenNavigator,\n    viewModel: DailyChallengeScreenViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    DailyChallengeScreen(\n        uiState = uiState,\n        onBackClick = destinationsNavigator::popBackStack,\n        onEvent = viewModel::onEvent,\n        navigateToGame = dailyChallengeScreenNavigator::navigateWithGameEvent\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\nprivate fun DailyChallengeScreen(\n    uiState: DailyChallengeScreenUiState,\n    onBackClick: () -> Unit = {},\n    onEvent: (event: DailyChallengeScreenUiEvent) -> Unit = {},\n    navigateToGame: (\n        event: GameEvent,\n        comparisonQuizCategories: List<ComparisonQuizCategory>\n    ) -> Unit\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val now by produceState(initialValue = Clock.System.now()) {\n        while (true) {\n            value = Clock.System.now()\n            delay(timeMillis = 1000)\n        }\n    }\n\n    val analyticsHelper = LocalAnalyticsHelper.current\n\n    Scaffold(\n        topBar = {\n            TopAppBar(\n                title = { Text(text = stringResource(id = CoreR.string.daily_challenge)) },\n                navigationIcon = { BackIconButton(onClick = onBackClick) }\n            )\n        }\n    ) { innerPadding ->\n        LazyColumn(\n            contentPadding = innerPadding + PaddingValues(spaceMedium),\n            verticalArrangement = Arrangement.spacedBy(spaceMedium)\n        ) {\n            items(\n                items = uiState.tasks,\n                key = { task -> task.id }\n            ) { task ->\n                DailyChallengeCard(\n                    now = now,\n                    title = task.title.asString(),\n                    currentValue = task.currentValue,\n                    maxValue = task.maxValue,\n                    dateRange = task.dateRange,\n                    isCompleted = task.isCompleted(),\n                    isClaimed = task.isClaimed,\n                    modifier = Modifier.fillParentMaxWidth(),\n                    diamondsReward = task.diamondsReward,\n                    userCanClaim = uiState.userAvailable,\n                    onClaimClick = {\n                        analyticsHelper.logEvent(\n                            AnalyticsEvent.DailyChallengeItemClaim(\n                                event = task.event,\n                                steps = task.currentValue.toInt()\n                            ),\n                            AnalyticsEvent.EarnDiamonds(earned = task.diamondsReward.toInt())\n                        )\n                        onEvent(DailyChallengeScreenUiEvent.OnClaimTaskClick(task.event))\n                    },\n                    onCardClick = {\n                        analyticsHelper.logEvent(\n                            AnalyticsEvent.DailyChallengeItemClick(event = task.event)\n                        )\n                        navigateToGame(task.event, uiState.comparisonQuizCategories)\n                    }\n                )\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun DailyChallengeScreenPreview() {\n    val now = Clock.System.now()\n\n    NewQuizTheme {\n        Surface {\n            DailyChallengeScreen(\n                uiState = DailyChallengeScreenUiState(\n                    tasks = List(10) {\n                        DailyChallengeTask(\n                            id = it,\n                            title = UiText.DynamicString(\"Task $it\"),\n                            diamondsReward = 10u,\n                            experienceReward = 100u,\n                            isClaimed = false,\n                            dateRange = now.minus(1.minutes)..now.plus(1.minutes),\n                            currentValue = (0..10).random().toUInt(),\n                            maxValue = 10u,\n                            event = GameEvent.MultiChoice.EndQuiz\n                        )\n                    }\n                ),\n                onBackClick = {},\n                onEvent = {},\n                navigateToGame = { _, _ -> }\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/daily-challenge/src/main/kotlin/com/infinitepower/newquiz/feature/daily_challenge/DailyChallengeScreenNavigator.kt",
    "content": "package com.infinitepower.newquiz.feature.daily_challenge\n\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\n\ninterface DailyChallengeScreenNavigator {\n    fun navigateWithGameEvent(\n        event: GameEvent,\n        comparisonQuizCategories: List<ComparisonQuizCategory>\n    ) {\n        when (event) {\n            // Navigate to random multi choice quiz\n            is GameEvent.MultiChoice.PlayRandomQuiz,\n            is GameEvent.MultiChoice.EndQuiz,\n            is GameEvent.MultiChoice.PlayQuestions,\n            is GameEvent.MultiChoice.GetAnswersCorrect -> {\n                navigateToMultiChoiceQuiz()\n            }\n\n            // Navigate to multi choice quiz with category\n            is GameEvent.MultiChoice.PlayQuizWithCategory -> {\n                val categoryKey = event.categoryId\n                val category = MultiChoiceBaseCategory.fromId(categoryKey)\n\n                navigateToMultiChoiceQuiz(category)\n            }\n\n            // Navigate to wordle quiz\n            is GameEvent.Wordle.GetWordCorrect -> {\n                navigateToWordleQuiz()\n            }\n            is GameEvent.Wordle.PlayWordWithCategory -> {\n                navigateToWordleQuiz(event.wordleCategory)\n            }\n\n            // Navigate to comparison quiz\n            is GameEvent.ComparisonQuiz.PlayAndGetScore -> {\n                val randomCategory = comparisonQuizCategories.random()\n                navigateToComparisonQuiz(randomCategory)\n            }\n            is GameEvent.ComparisonQuiz.PlayQuizWithCategory -> {\n                val categoryId = event.categoryId\n                val category = comparisonQuizCategories.find { it.id == categoryId } ?: return\n\n                navigateToComparisonQuiz(category)\n            }\n            is GameEvent.ComparisonQuiz.PlayWithComparisonMode -> {\n                val randomCategory = comparisonQuizCategories.random()\n\n                navigateToComparisonQuiz(randomCategory, event.mode)\n            }\n        }\n    }\n\n    fun navigateToMultiChoiceQuiz(\n        category: MultiChoiceBaseCategory = MultiChoiceBaseCategory.Random\n    )\n\n    fun navigateToWordleQuiz(\n        type: WordleQuizType = WordleQuizType.entries.random()\n    )\n\n    fun navigateToComparisonQuiz(\n        category: ComparisonQuizCategory,\n        mode: ComparisonMode = ComparisonMode.entries.random()\n    )\n}\n"
  },
  {
    "path": "feature/daily-challenge/src/main/kotlin/com/infinitepower/newquiz/feature/daily_challenge/DailyChallengeScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.feature.daily_challenge\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.global_event.GameEvent\n\ninterface DailyChallengeScreenUiEvent {\n    @Keep\n    data class OnClaimTaskClick(\n        val taskType: GameEvent\n    ) : DailyChallengeScreenUiEvent\n}\n"
  },
  {
    "path": "feature/daily-challenge/src/main/kotlin/com/infinitepower/newquiz/feature/daily_challenge/DailyChallengeScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.feature.daily_challenge\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.daily_challenge.DailyChallengeTask\n\n@Keep\ndata class DailyChallengeScreenUiState(\n    val loading: Boolean = true,\n    val tasks: List<DailyChallengeTask> = emptyList(),\n    val comparisonQuizCategories: List<ComparisonQuizCategory> = emptyList(),\n    val userAvailable: Boolean = false\n)\n"
  },
  {
    "path": "feature/daily-challenge/src/main/kotlin/com/infinitepower/newquiz/feature/daily_challenge/DailyChallengeScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.feature.daily_challenge\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.daily_challenge.DailyChallengeRepository\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.stateIn\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\nclass DailyChallengeScreenViewModel @Inject constructor(\n    private val dailyChallengeRepository: DailyChallengeRepository,\n    private val comparisonQuizRepository: ComparisonQuizRepository,\n    private val userService: UserService\n) : ViewModel() {\n    private val _uiState = MutableStateFlow(DailyChallengeScreenUiState())\n    val uiState = combine(\n        _uiState,\n        dailyChallengeRepository.getAvailableTasksFlow()\n    ) { uiState, tasks ->\n        uiState.copy(\n            // Sort tasks by first claimable and claimed at the end\n            tasks = tasks\n                .sortedByDescending { it.isClaimable() }\n                .sortedBy { it.isClaimed }\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(),\n        initialValue = DailyChallengeScreenUiState()\n    )\n\n    init {\n        viewModelScope.launch {\n            _uiState.update { currentState ->\n                currentState.copy(\n                    loading = false,\n                    comparisonQuizCategories = comparisonQuizRepository.getCategories(),\n                    userAvailable = userService.userAvailable()\n                )\n            }\n        }\n    }\n\n    fun onEvent(event: DailyChallengeScreenUiEvent) {\n        when (event) {\n            is DailyChallengeScreenUiEvent.OnClaimTaskClick -> claimTask(event.taskType)\n        }\n    }\n\n    private fun claimTask(taskType: GameEvent) {\n        viewModelScope.launch {\n            dailyChallengeRepository.claimTask(taskType)\n        }\n    }\n}\n"
  },
  {
    "path": "feature/daily-challenge/src/main/kotlin/com/infinitepower/newquiz/feature/daily_challenge/components/DailyChallengeCard.kt",
    "content": "package com.infinitepower.newquiz.feature.daily_challenge.components\n\nimport android.text.format.DateUtils\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.LinearProgressIndicator\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedCard\nimport androidx.compose.material3.ProgressIndicatorDefaults\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.StrokeCap\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport kotlinx.datetime.Clock\nimport kotlinx.datetime.Instant\nimport kotlin.time.Duration.Companion.minutes\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun DailyChallengeCard(\n    modifier: Modifier = Modifier,\n    onClaimClick: () -> Unit,\n    onCardClick: () -> Unit,\n    now: Instant,\n    title: String,\n    currentValue: UInt,\n    maxValue: UInt,\n    dateRange: ClosedRange<Instant>,\n    isCompleted: Boolean,\n    isClaimed: Boolean,\n    diamondsReward: UInt,\n    userCanClaim: Boolean = true,\n) {\n    val isExpired = remember(dateRange, now) {\n        now > dateRange.endInclusive\n    }\n\n    val remainingTime = remember(dateRange, now) {\n        val remaining = dateRange.endInclusive - now\n\n        if (remaining.isNegative()) {\n            \"0\"\n        } else {\n            DateUtils.formatElapsedTime(remaining.inWholeSeconds)\n        }\n    }\n\n    DailyChallengeCard(\n        modifier = modifier,\n        title = title,\n        currentValue = currentValue,\n        maxValue = maxValue,\n        remainingTimeInSeconds = remainingTime,\n        isCompleted = isCompleted,\n        enabled = !isExpired && !isClaimed,\n        userCanClaim = userCanClaim,\n        diamondsReward = diamondsReward,\n        onClaimClick = onClaimClick,\n        onCardClick = onCardClick\n    )\n}\n\n@Composable\ninternal fun DailyChallengeCard(\n    modifier: Modifier = Modifier,\n    onClaimClick: () -> Unit,\n    onCardClick: () -> Unit,\n    title: String,\n    currentValue: UInt,\n    maxValue: UInt,\n    remainingTimeInSeconds: String,\n    isCompleted: Boolean,\n    diamondsReward: UInt,\n    enabled: Boolean = true,\n    userCanClaim: Boolean = true,\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val progress = remember(currentValue, maxValue) {\n        currentValue.toFloat() / maxValue.toFloat()\n    }\n\n    val progressColor = if (enabled) {\n        ProgressIndicatorDefaults.linearColor\n    } else {\n        ProgressIndicatorDefaults.linearColor.copy(alpha = 0.3f)\n    }\n\n    val trackColor = if (enabled) {\n        ProgressIndicatorDefaults.linearTrackColor\n    } else {\n        ProgressIndicatorDefaults.linearTrackColor.copy(alpha = 0.3f)\n    }\n\n    OutlinedCard(\n        modifier = modifier,\n        shape = MaterialTheme.shapes.medium,\n        onClick = onCardClick,\n        enabled = enabled\n    ) {\n        Column(\n            modifier = Modifier.padding(spaceMedium)\n        ) {\n            Text(\n                text = title,\n                style = MaterialTheme.typography.titleMedium\n            )\n            Spacer(modifier = Modifier.height(spaceMedium))\n            Row(\n                modifier = Modifier.fillMaxWidth(),\n                horizontalArrangement = Arrangement.SpaceBetween,\n                verticalAlignment = Alignment.CenterVertically\n            ) {\n                Text(\n                    text = stringResource(\n                        id = CoreR.string.remaining_time,\n                        remainingTimeInSeconds\n                    ),\n                    style = MaterialTheme.typography.bodyMedium\n                )\n\n                Text(\n                    text = \"$currentValue / $maxValue\",\n                    style = MaterialTheme.typography.bodyMedium\n                )\n            }\n            Spacer(modifier = Modifier.height(spaceMedium))\n            LinearProgressIndicator(\n                progress = { progress },\n                modifier = Modifier\n                    .height(8.dp)\n                    .fillMaxWidth(),\n                color = progressColor,\n                trackColor = trackColor,\n                strokeCap = StrokeCap.Round,\n            )\n            if (isCompleted && enabled && userCanClaim) {\n                Spacer(modifier = Modifier.height(spaceMedium))\n                Button(\n                    onClick = onClaimClick,\n                    modifier = Modifier.align(Alignment.End)\n                ) {\n                    Text(\n                        text = stringResource(\n                            id = CoreR.string.claim_n_diamonds,\n                            diamondsReward.toInt()\n                        )\n                    )\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun DailyChallengeCardPreview() {\n    val now = Clock.System.now()\n\n    NewQuizTheme {\n        Surface {\n            DailyChallengeCard(\n                modifier = Modifier.padding(16.dp),\n                now = now,\n                title = \"Daily Challenge\",\n                currentValue = 8u,\n                maxValue = 10u,\n                dateRange = now.minus(1.minutes)..now.plus(1.minutes),\n                isCompleted = true,\n                isClaimed = false,\n                diamondsReward = 10u,\n                onClaimClick = {},\n                onCardClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/.gitignore",
    "content": "/build"
  },
  {
    "path": "feature/maze/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.feature)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.feature.maze\"\n}\n\ndependencies {\n    implementation(libs.androidx.work.ktx)\n\n    implementation(libs.androidx.lifecycle.livedata.ktx)\n\n    implementation(libs.androidx.compose.material.iconsExtended)\n    implementation(libs.androidx.graphics.shapes)\n\n    implementation(libs.lottie.compose)\n\n    implementation(projects.core.datastore)\n    implementation(projects.data)\n    implementation(projects.domain)\n    testImplementation(projects.core.testing)\n}\n"
  },
  {
    "path": "feature/maze/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/MazeScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.maze\n\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.ContentCopy\nimport androidx.compose.material.icons.rounded.Info\nimport androidx.compose.material.icons.rounded.MoreVert\nimport androidx.compose.material.icons.rounded.RestartAlt\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.DropdownMenu\nimport androidx.compose.material3.DropdownMenuItem\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.LinearProgressIndicator\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.LocalClipboardManager\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.text.AnnotatedString\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.navigation.MazeNavigator\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.SnackbarController\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.feature.maze.components.CategoriesInfoBottomSheet\nimport com.infinitepower.newquiz.feature.maze.components.InvalidCategoriesCard\nimport com.infinitepower.newquiz.feature.maze.components.MazeCompletedCard\nimport com.infinitepower.newquiz.feature.maze.components.MazePath\nimport com.infinitepower.newquiz.feature.maze.generate.GenerateMazeScreen\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport com.infinitepower.newquiz.model.maze.MazeQuiz.MazeItem\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport com.ramcosta.composedestinations.annotation.DeepLink\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.collections.immutable.toPersistentList\nimport kotlinx.coroutines.launch\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@Destination(\n    deepLinks = [\n        DeepLink(uriPattern = \"newquiz://maze\")\n    ]\n)\n@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)\nfun MazeScreen(\n    navigator: DestinationsNavigator,\n    mazeNavigator: MazeNavigator,\n    viewModel: MazeScreenViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    MazeScreenImpl(\n        uiState = uiState,\n        navigateBack = navigator::popBackStack,\n        uiEvent = viewModel::onEvent,\n        onItemClick = mazeNavigator::navigateToGame,\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\n@ExperimentalFoundationApi\nprivate fun MazeScreenImpl(\n    uiState: MazeScreenUiState,\n    navigateBack: () -> Unit,\n    uiEvent: (event: MazeScreenUiEvent) -> Unit,\n    onItemClick: (item: MazeItem) -> Unit,\n) {\n    when {\n        uiState.loading -> {\n            Box {\n                LinearProgressIndicator(modifier = Modifier.fillMaxWidth())\n            }\n        }\n\n        !uiState.loading && uiState.isMazeEmpty -> GenerateMazeScreen(onBackClick = navigateBack)\n        !uiState.loading && !uiState.isMazeEmpty -> {\n            MazePathScreen(\n                uiState = uiState,\n                navigateBack = navigateBack,\n                uiEvent = uiEvent,\n                onItemClick = onItemClick\n            )\n        }\n    }\n}\n\n@Composable\n@ExperimentalMaterial3Api\n@ExperimentalFoundationApi\nprivate fun MazePathScreen(\n    uiState: MazeScreenUiState,\n    navigateBack: () -> Unit,\n    uiEvent: (event: MazeScreenUiEvent) -> Unit,\n    onItemClick: (item: MazeItem) -> Unit\n) {\n    val clipboardManager = LocalClipboardManager.current\n    val spaceMedium = MaterialTheme.spacing.medium\n    val scope = rememberCoroutineScope()\n\n    var moreOptionsExpanded by remember { mutableStateOf(false) }\n    var categoriesInfoBottomSheetVisible by remember { mutableStateOf(false) }\n\n    var restartMazeDialogVisible by remember { mutableStateOf(false) }\n    val showRestartMazeDialog = { restartMazeDialogVisible = true }\n\n    val categoriesByGameMode = remember(uiState.maze) {\n        uiState.getAvailableCategoriesByGameMode()\n    }\n\n    val invalidQuestions = remember(categoriesByGameMode) {\n        uiState.getInvalidMazeItems(categoriesByGameMode)\n    }\n\n    Scaffold(\n        topBar = {\n            TopAppBar(\n                title = {\n                    Text(text = stringResource(id = CoreR.string.maze))\n                },\n                navigationIcon = { BackIconButton(onClick = navigateBack) },\n                actions = {\n                    IconButton(onClick = { moreOptionsExpanded = true }) {\n                        Icon(\n                            imageVector = Icons.Rounded.MoreVert,\n                            contentDescription = stringResource(id = CoreR.string.more_options)\n                        )\n                    }\n                    DropdownMenu(\n                        expanded = moreOptionsExpanded,\n                        onDismissRequest = { moreOptionsExpanded = false }\n                    ) {\n                        if (!uiState.isMazeEmpty) {\n                            DropdownMenuItem(\n                                text = { Text(text = stringResource(id = CoreR.string.category_information)) },\n                                onClick = {\n                                    moreOptionsExpanded = false\n                                    categoriesInfoBottomSheetVisible = true\n                                },\n                                leadingIcon = {\n                                    Icon(\n                                        imageVector = Icons.Rounded.Info,\n                                        contentDescription = null\n                                    )\n                                }\n                            )\n                            uiState.mazeSeed?.let { mazeSeed ->\n                                DropdownMenuItem(\n                                    text = { Text(stringResource(id = CoreR.string.copy_maze_seed)) },\n                                    onClick = {\n                                        clipboardManager.setText(AnnotatedString(mazeSeed.toString()))\n                                        moreOptionsExpanded = false\n                                    },\n                                    leadingIcon = {\n                                        Icon(\n                                            imageVector = Icons.Rounded.ContentCopy,\n                                            contentDescription = stringResource(id = CoreR.string.copy_maze_seed)\n                                        )\n                                    }\n                                )\n                            }\n                            DropdownMenuItem(\n                                text = { Text(stringResource(id = CoreR.string.restart_maze)) },\n                                onClick = {\n                                    showRestartMazeDialog()\n                                    moreOptionsExpanded = false\n                                },\n                                leadingIcon = {\n                                    Icon(\n                                        imageVector = Icons.Rounded.RestartAlt,\n                                        contentDescription = stringResource(id = CoreR.string.restart_maze)\n                                    )\n                                }\n                            )\n                        }\n                    }\n                },\n            )\n        }\n    ) { innerPadding ->\n        if (!uiState.isMazeEmpty) {\n            Column(\n                modifier = Modifier\n                    .padding(innerPadding)\n                    .fillMaxSize(),\n            ) {\n                MazePath(\n                    modifier = Modifier.weight(1f),\n                    items = uiState.maze.items,\n                    mazeSeed = uiState.mazeSeed ?: 0,\n                    onItemClick = { item ->\n                        if (item in invalidQuestions) {\n                            scope.launch {\n                                SnackbarController.sendShortMessage(\n                                    UiText.StringResource(CoreR.string.invalid_questions)\n                                )\n                            }\n                        } else {\n                            onItemClick(item)\n                        }\n                    },\n                    startScrollToCurrentItem = uiState.autoScrollToCurrentItem\n                )\n\n                if (invalidQuestions.isNotEmpty()) {\n                    InvalidCategoriesCard(\n                        invalidQuestionsCount = invalidQuestions.size,\n                        onRemoveClick = { uiEvent(MazeScreenUiEvent.RemoveInvalidCategories) },\n                        onRestartClick = showRestartMazeDialog,\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(\n                                start = spaceMedium,\n                                end = spaceMedium,\n                                bottom = spaceMedium\n                            )\n                    )\n                }\n\n                if (uiState.isMazeCompleted) {\n                    MazeCompletedCard(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .background(MaterialTheme.colorScheme.background)\n                            .padding(\n                                start = spaceMedium,\n                                end = spaceMedium,\n                                bottom = spaceMedium\n                            ),\n                        onRestartClick = showRestartMazeDialog\n                    )\n                }\n            }\n        }\n    }\n\n    if (categoriesInfoBottomSheetVisible) {\n        CategoriesInfoBottomSheet(\n            onDismissRequest = { categoriesInfoBottomSheetVisible = false },\n            categoriesByGameMode = categoriesByGameMode\n        )\n    }\n\n    if (restartMazeDialogVisible) {\n        RestartMazeDialog(\n            onDismissRequest = { restartMazeDialogVisible = false },\n            onConfirm = { uiEvent(MazeScreenUiEvent.RestartMaze) }\n        )\n    }\n}\n\n@Composable\nprivate fun RestartMazeDialog(\n    onDismissRequest: () -> Unit,\n    onConfirm: () -> Unit\n) {\n    AlertDialog(\n        title = { Text(text = stringResource(CoreR.string.restart_maze)) },\n        text = { Text(text = stringResource(CoreR.string.restart_maze_dialog_description)) },\n        onDismissRequest = onDismissRequest,\n        confirmButton = {\n            TextButton(onClick = onConfirm) {\n                Text(text = stringResource(CoreR.string.restart))\n            }\n        },\n        dismissButton = {\n            TextButton(onClick = onDismissRequest) {\n                Text(text = stringResource(CoreR.string.cancel))\n            }\n        }\n    )\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)\nprivate fun MazeScreenPreview() {\n    val completedItems = List(3) {\n        MazeItem.Wordle(\n            wordleWord = WordleWord(\"1+1=2\"),\n            difficulty = QuestionDifficulty.Easy,\n            played = true,\n            wordleQuizType = WordleQuizType.MATH_FORMULA,\n            mazeSeed = 0\n        )\n    }\n\n    val otherItems = List(8) {\n        MazeItem.Wordle(\n            wordleWord = WordleWord(\"1+1=2\"),\n            difficulty = QuestionDifficulty.Easy,\n            wordleQuizType = WordleQuizType.MATH_FORMULA,\n            mazeSeed = 0\n        )\n    }\n\n    val mazeItems = (completedItems + otherItems).toPersistentList()\n\n    NewQuizTheme {\n        Surface {\n            MazeScreenImpl(\n                uiState = MazeScreenUiState(\n                    loading = false,\n                    maze = MazeQuiz(items = mazeItems)\n                ),\n                navigateBack = {},\n                uiEvent = {},\n                onItemClick = {},\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/MazeScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.feature.maze\n\nsealed interface MazeScreenUiEvent {\n    data object RestartMaze : MazeScreenUiEvent\n\n    data object RemoveInvalidCategories : MazeScreenUiEvent\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/MazeScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.feature.maze\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.feature.maze.common.MazeCategories\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport com.infinitepower.newquiz.model.maze.emptyMaze\n\ntypealias CategoriesByGameMode = Map<GameMode, Set<BaseCategory>>\n\n@Keep\ndata class MazeScreenUiState(\n    val loading: Boolean = true,\n    val maze: MazeQuiz = emptyMaze(),\n    val autoScrollToCurrentItem: Boolean = true,\n    val comparisonQuizCategories: List<BaseCategory> = emptyList(),\n) {\n    val isMazeEmpty: Boolean\n        get() = maze.items.isEmpty()\n\n    val isMazeCompleted: Boolean\n        get() = maze.items.all { it.played }\n\n    val mazeSeed: Int?\n        get() = maze.items.firstOrNull()?.mazeSeed\n\n    fun getAvailableCategoriesByGameMode(): CategoriesByGameMode {\n        return maze.items.groupBy { it.gameMode }.mapValues { (gameMode, categoryItems) ->\n            if (categoryItems.isEmpty()) return@mapValues emptySet()\n\n            val availableCategories = when (gameMode) {\n                GameMode.MULTI_CHOICE -> MazeCategories.getMultiChoiceCategories()\n                GameMode.WORDLE -> MazeCategories.availableWordleCategories\n                GameMode.COMPARISON_QUIZ -> comparisonQuizCategories\n            }\n\n            categoryItems.mapNotNull { item ->\n                availableCategories.find { it.id == item.categoryId }\n            }.toSet()\n        }\n    }\n\n    fun getInvalidMazeItems(availableCategories: CategoriesByGameMode): List<MazeQuiz.MazeItem> {\n        return maze.items.filter { item ->\n            val categoriesForGameMode = availableCategories[item.gameMode] ?: emptyList()\n            !categoriesForGameMode.any { it.id == item.categoryId }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/MazeScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.feature.maze\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport androidx.work.WorkManager\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.data.worker.maze.CleanMazeQuizWorker\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.model.BaseCategory\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.flow.stateIn\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\nclass MazeScreenViewModel @Inject constructor(\n    private val mazeMathQuizRepository: MazeQuizRepository,\n    private val workManager: WorkManager,\n    private val analyticsHelper: AnalyticsHelper,\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager,\n    private val comparisonQuizRepository: ComparisonQuizRepository\n) : ViewModel() {\n    private val _uiState = MutableStateFlow(MazeScreenUiState())\n    val uiState = combine(\n        _uiState,\n        mazeMathQuizRepository.getSavedMazeQuizFlow()\n    ) { uiState, savedMazeQuiz ->\n        uiState.copy(\n            maze = savedMazeQuiz,\n            loading = false\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000),\n        initialValue = MazeScreenUiState()\n    )\n\n    init {\n        viewModelScope.launch {\n            val autoScrollToCurrentItem = settingsDataStoreManager.getPreference(\n                preferenceEntry = SettingsCommon.MazeAutoScrollToCurrentItem\n            )\n\n            _uiState.update { currentState ->\n                currentState.copy(\n                    autoScrollToCurrentItem = autoScrollToCurrentItem,\n                    comparisonQuizCategories = getComparisonQuizCategories()\n                )\n            }\n        }\n    }\n\n    fun onEvent(event: MazeScreenUiEvent) {\n        when (event) {\n            is MazeScreenUiEvent.RestartMaze -> cleanSavedMaze()\n            is MazeScreenUiEvent.RemoveInvalidCategories -> viewModelScope.launch {\n                removeInvalidQuestions()\n            }\n        }\n    }\n\n    private fun cleanSavedMaze() {\n        analyticsHelper.logEvent(AnalyticsEvent.RestartMaze)\n\n        CleanMazeQuizWorker.enqueue(workManager)\n    }\n\n    private suspend fun removeInvalidQuestions() {\n        val state = uiState.first()\n        val availableCategories = state.getAvailableCategoriesByGameMode()\n        val invalidItems = state.getInvalidMazeItems(availableCategories)\n        mazeMathQuizRepository.removeItems(invalidItems)\n    }\n\n    private fun getComparisonQuizCategories(): List<BaseCategory> {\n        return comparisonQuizRepository.getCategories().filterNot { it.isMazeDisabled }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/categories_info/MazeCategoriesInfoScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.categories_info\n\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.LazyListScope\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.category.CategoryComponent\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.local.wordle.WordleCategories\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.toImmutableList\n\n@Composable\n@Destination\nfun MazeCategoriesInfoScreen(\n    navigator: DestinationsNavigator,\n    viewModel: MazeCategoriesInfoViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    MazeCategoriesInfoScreenImpl(\n        onBackClick = navigator::popBackStack,\n        uiState = uiState\n    )\n}\n\n@Composable\n@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)\nprivate fun MazeCategoriesInfoScreenImpl(\n    uiState: MazeCategoriesInfoUiState,\n    onBackClick: () -> Unit = {},\n) {\n    Scaffold(\n        topBar = {\n            TopAppBar(\n                title = { Text(text = \"Categories Info\") },\n                navigationIcon = { BackIconButton(onClick = onBackClick) }\n            )\n        }\n    ) { innerPadding ->\n        Column(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(innerPadding),\n        ) {\n            if (uiState.loading) {\n                CircularProgressIndicator()\n            } else {\n                CategoriesContent(uiState = uiState)\n            }\n        }\n    }\n}\n\n@Composable\n@ExperimentalFoundationApi\nprivate fun CategoriesContent(\n    uiState: MazeCategoriesInfoUiState,\n) {\n    val multiChoiceHeader = stringResource(id = R.string.multi_choice_quiz)\n    val wordleHeader = stringResource(id = R.string.wordle)\n    val comparisonQuizHeader = stringResource(id = R.string.comparison_quiz)\n\n    LazyColumn(\n        contentPadding = PaddingValues(bottom = MaterialTheme.spacing.medium),\n        verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.medium)\n    ) {\n        categoriesItemsWithHeader(\n            title = multiChoiceHeader,\n            categories = uiState.multiChoiceCategories\n        )\n        categoriesItemsWithHeader(\n            title = wordleHeader,\n            categories = uiState.wordleCategories\n        )\n        categoriesItemsWithHeader(\n            title = comparisonQuizHeader,\n            categories = uiState.comparisonQuizCategories\n        )\n    }\n}\n\n@ExperimentalFoundationApi\nprivate fun LazyListScope.categoriesItemsWithHeader(\n    title: String,\n    categories: ImmutableList<BaseCategory>\n) {\n    if (categories.isNotEmpty()) {\n        categoriesStickyHeader(title = title)\n        categoriesItems(categories = categories, baseItemKey = title)\n    }\n}\n\n@ExperimentalFoundationApi\nprivate fun LazyListScope.categoriesStickyHeader(\n    title: String\n) {\n    stickyHeader {\n        Text(\n            text = title,\n            style = MaterialTheme.typography.headlineMedium,\n            modifier = Modifier\n                .fillParentMaxWidth()\n                .background(color = MaterialTheme.colorScheme.surface)\n                .padding(\n                    horizontal = MaterialTheme.spacing.medium,\n                    vertical = MaterialTheme.spacing.small\n                )\n        )\n    }\n}\n\nprivate fun <T : BaseCategory> LazyListScope.categoriesItems(\n    categories: ImmutableList<T>,\n    baseItemKey: String = \"\",\n) {\n    items(\n        items = categories,\n        key = { category -> \"$baseItemKey-${category.id}\" }\n    ) { category ->\n        CategoryComponent(\n            title = category.name.asString(),\n            imageUrl = category.image,\n            requireInternetConnection = category.requireInternetConnection,\n            showConnectionInfo = ShowCategoryConnectionInfo.BOTH,\n            modifier = Modifier\n                .fillParentMaxWidth()\n                .padding(horizontal = MaterialTheme.spacing.medium),\n            clickEnabled = false\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun MazeCategoriesInfoScreenPreview() {\n    NewQuizTheme {\n        MazeCategoriesInfoScreenImpl(\n            uiState = MazeCategoriesInfoUiState(\n                loading = false,\n                multiChoiceCategories = multiChoiceQuestionCategories.take(2).toImmutableList(),\n                wordleCategories = WordleCategories.allCategories.take(1).toImmutableList(),\n            )\n        )\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/categories_info/MazeCategoriesInfoUiState.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.categories_info\n\nimport com.infinitepower.newquiz.model.BaseCategory\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\n\ndata class MazeCategoriesInfoUiState(\n    val loading: Boolean = true,\n    val multiChoiceCategories: ImmutableList<BaseCategory> = persistentListOf(),\n    val wordleCategories: ImmutableList<BaseCategory> = persistentListOf(),\n    val comparisonQuizCategories: ImmutableList<BaseCategory> = persistentListOf(),\n)\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/categories_info/MazeCategoriesInfoViewModel.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.categories_info\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.feature.maze.common.MazeCategories\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.collections.immutable.toImmutableList\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.map\nimport kotlinx.coroutines.flow.stateIn\nimport javax.inject.Inject\n\n@HiltViewModel\nclass MazeCategoriesInfoViewModel @Inject constructor(\n    private val mazeMathQuizRepository: MazeQuizRepository,\n    private val comparisonQuizRepository: ComparisonQuizRepository\n) : ViewModel() {\n    private val categoriesByGameMode = mazeMathQuizRepository\n        .getSavedMazeQuizFlow()\n        .map { quiz ->\n            quiz.items\n                .groupBy { item -> item.gameMode }\n                .mapValues { (gameMode, categoryItems) ->\n                    val categoriesIds = categoryItems.map { it.categoryId }.toSet()\n\n                    if (categoriesIds.isEmpty()) return@mapValues emptyList()\n\n                    val categories = when (gameMode) {\n                        GameMode.MULTI_CHOICE -> MazeCategories.getMultiChoiceCategories()\n                        GameMode.WORDLE -> MazeCategories.availableWordleCategories\n                        GameMode.COMPARISON_QUIZ -> getComparisonQuizCategories()\n                    }\n\n                    categories.filter { category -> categoriesIds.contains(category.id) }\n                }\n        }\n\n    val uiState = categoriesByGameMode.map {\n        MazeCategoriesInfoUiState(\n            multiChoiceCategories = it[GameMode.MULTI_CHOICE].orEmpty().toImmutableList(),\n            wordleCategories = it[GameMode.WORDLE].orEmpty().toImmutableList(),\n            comparisonQuizCategories = it[GameMode.COMPARISON_QUIZ].orEmpty().toImmutableList(),\n            loading = false\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000),\n        initialValue = MazeCategoriesInfoUiState()\n    )\n\n    private fun getComparisonQuizCategories(): List<BaseCategory> {\n        return comparisonQuizRepository.getCategories().filterNot { it.isMazeDisabled }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/common/MazeCategories.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.common\n\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.local.wordle.WordleCategories\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceCategory\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.core.R as CoreR\n\nobject MazeCategories {\n    private val availableMultiChoiceCategoriesIds = listOf(\n        MultiChoiceBaseCategory.Logo.id,\n        MultiChoiceBaseCategory.Flag.id,\n        MultiChoiceBaseCategory.CountryCapitalFlags.id,\n        MultiChoiceBaseCategory.GuessMathSolution.id,\n    )\n\n    fun getMultiChoiceCategories(): List<MultiChoiceCategory> {\n        val filteredCategories = multiChoiceQuestionCategories.filter { category ->\n            availableMultiChoiceCategoriesIds.contains(category.id)\n        }\n\n        // Because with the implementation with OpenTriviaDB, we can't select\n        // specific category, so we need to create a special category\n        // for this case, that contains all the categories.\n        val othersCategory = MultiChoiceCategory(\n            id = \"others\",\n            name = UiText.DynamicString(\"Others\"),\n            image = CoreR.drawable.general_knowledge,\n            requireInternetConnection = true\n        )\n\n        return filteredCategories + othersCategory\n    }\n\n    val availableWordleCategories = WordleCategories.allCategories.filter { category ->\n        category.id != WordleQuizType.NUMBER_TRIVIA.name\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/components/CategoriesInfoBottomSheet.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.components\n\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.LazyListScope\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.BottomSheetDefaults\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.ModalBottomSheet\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.category.CategoryComponent\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.local.wordle.WordleCategories\nimport com.infinitepower.newquiz.feature.maze.CategoriesByGameMode\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\nimport kotlinx.collections.immutable.toImmutableList\n\n@Composable\n@ExperimentalMaterial3Api\n@ExperimentalFoundationApi\ninternal fun CategoriesInfoBottomSheet(\n    onDismissRequest: () -> Unit,\n    categoriesByGameMode: CategoriesByGameMode\n) {\n    ModalBottomSheet(\n        onDismissRequest = onDismissRequest\n    ) {\n        CategoriesContent(\n            multiChoiceCategories = categoriesByGameMode[GameMode.MULTI_CHOICE].orEmpty().toImmutableList(),\n            wordleCategories = categoriesByGameMode[GameMode.WORDLE].orEmpty().toImmutableList(),\n            comparisonQuizCategories = categoriesByGameMode[GameMode.COMPARISON_QUIZ].orEmpty().toImmutableList()\n        )\n    }\n}\n\n@Composable\n@ExperimentalMaterial3Api\n@ExperimentalFoundationApi\nprivate fun CategoriesContent(\n    modifier: Modifier = Modifier,\n    multiChoiceCategories: ImmutableList<BaseCategory>,\n    wordleCategories: ImmutableList<BaseCategory>,\n    comparisonQuizCategories: ImmutableList<BaseCategory>,\n) {\n    val multiChoiceHeader = stringResource(id = R.string.multi_choice_quiz)\n    val wordleHeader = stringResource(id = R.string.wordle)\n    val comparisonQuizHeader = stringResource(id = R.string.comparison_quiz)\n\n    LazyColumn(\n        modifier = modifier,\n        contentPadding = PaddingValues(bottom = MaterialTheme.spacing.medium),\n        verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.medium)\n    ) {\n        categoriesItemsWithHeader(\n            title = multiChoiceHeader,\n            categories = multiChoiceCategories\n        )\n        categoriesItemsWithHeader(\n            title = wordleHeader,\n            categories = wordleCategories\n        )\n        categoriesItemsWithHeader(\n            title = comparisonQuizHeader,\n            categories = comparisonQuizCategories\n        )\n    }\n}\n\n@ExperimentalMaterial3Api\n@ExperimentalFoundationApi\nprivate fun LazyListScope.categoriesItemsWithHeader(\n    title: String,\n    categories: ImmutableList<BaseCategory>\n) {\n    if (categories.isNotEmpty()) {\n        categoriesStickyHeader(title = title)\n        categoriesItems(categories = categories)\n    }\n}\n\n@ExperimentalMaterial3Api\n@ExperimentalFoundationApi\nprivate fun LazyListScope.categoriesStickyHeader(\n    title: String,\n) {\n    stickyHeader {\n        Text(\n            text = title,\n            style = MaterialTheme.typography.headlineMedium,\n            modifier = Modifier\n                .fillParentMaxWidth()\n                .background(color = BottomSheetDefaults.ContainerColor)\n                .padding(\n                    horizontal = MaterialTheme.spacing.medium,\n                    vertical = MaterialTheme.spacing.small\n                )\n        )\n    }\n}\n\nprivate fun <T : BaseCategory> LazyListScope.categoriesItems(\n    categories: ImmutableList<T>,\n) {\n    items(items = categories) { category ->\n        CategoryComponent(\n            title = category.name.asString(),\n            imageUrl = category.image,\n            requireInternetConnection = category.requireInternetConnection,\n            showConnectionInfo = ShowCategoryConnectionInfo.BOTH,\n            modifier = Modifier\n                .fillParentMaxWidth()\n                .padding(horizontal = MaterialTheme.spacing.medium),\n            clickEnabled = false\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)\nprivate fun MazeCategoriesContentPreview() {\n    NewQuizTheme {\n        Surface {\n            CategoriesContent(\n                multiChoiceCategories = multiChoiceQuestionCategories.take(2).toImmutableList(),\n                wordleCategories = WordleCategories.allCategories.take(1).toImmutableList(),\n                comparisonQuizCategories = persistentListOf()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/components/InvalidCategoriesCard.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Warning\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun InvalidCategoriesCard(\n    invalidQuestionsCount: Int,\n    onRemoveClick: () -> Unit = {},\n    onRestartClick: () -> Unit = {},\n    modifier: Modifier = Modifier\n) {\n    Surface(\n        modifier = modifier,\n        color = MaterialTheme.colorScheme.errorContainer,\n        shape = MaterialTheme.shapes.medium\n    ) {\n        Column(\n            modifier = Modifier.padding(MaterialTheme.spacing.medium),\n            verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.extraSmall)\n        ) {\n            Row(\n                verticalAlignment = Alignment.CenterVertically,\n                horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.medium)\n            ) {\n                Icon(\n                    imageVector = Icons.Rounded.Warning,\n                    contentDescription = null,\n                    tint = MaterialTheme.colorScheme.error,\n                    modifier = Modifier.size(48.dp)\n                )\n                Column(\n                    verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.small)\n                ) {\n                    Text(\n                        text = stringResource(CoreR.string.invalid_questions),\n                        style = MaterialTheme.typography.titleMedium\n                    )\n                    Text(\n                        text = stringResource(CoreR.string.invalid_questions, invalidQuestionsCount),\n                        style = MaterialTheme.typography.bodyMedium\n                    )\n                }\n            }\n            Spacer(modifier = Modifier.height(MaterialTheme.spacing.small))\n            Row(\n                modifier = Modifier.align(Alignment.End),\n                horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.small)\n            ) {\n                TextButton(\n                    onClick = onRestartClick,\n                    colors = ButtonDefaults.textButtonColors(\n                        contentColor = MaterialTheme.colorScheme.error\n                    )\n                ) {\n                    Text(text = stringResource(CoreR.string.restart_maze))\n                }\n                Button(\n                    onClick = onRemoveClick,\n                    colors = ButtonDefaults.buttonColors(\n                        containerColor = MaterialTheme.colorScheme.error,\n                        contentColor = MaterialTheme.colorScheme.onError\n                    ),\n                ) {\n                    Text(text = stringResource(CoreR.string.remove_them))\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun InvalidCategoriesCardPreview() {\n    NewQuizTheme {\n        Surface {\n            InvalidCategoriesCard(\n                invalidQuestionsCount = 3,\n                modifier = Modifier\n                    .padding(MaterialTheme.spacing.medium)\n                    .fillMaxWidth()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/components/MazeCompletedCard.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.icons.Trophy\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun MazeCompletedCard(\n    onRestartClick: () -> Unit = {},\n    modifier: Modifier = Modifier\n) {\n    Surface(\n        modifier = modifier,\n        color = MaterialTheme.colorScheme.primaryContainer,\n        shape = MaterialTheme.shapes.medium\n    ) {\n        Row(\n            modifier = Modifier.padding(MaterialTheme.spacing.medium),\n            verticalAlignment = Alignment.CenterVertically,\n            horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.medium)\n        ) {\n            Icon(\n                imageVector = Trophy,\n                contentDescription = null,\n                tint = MaterialTheme.colorScheme.primary,\n                modifier = Modifier.size(48.dp)\n            )\n            Column(\n                verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.extraSmall)\n            ) {\n                Text(\n                    text =  stringResource(id = CoreR.string.maze_completed),\n                    style = MaterialTheme.typography.titleMedium\n                )\n                Button(onClick = onRestartClick) {\n                    Text(text = stringResource(id = CoreR.string.restart_maze))\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun MazeCompletedCardPreview() {\n    NewQuizTheme {\n        Surface {\n            MazeCompletedCard(\n                modifier = Modifier\n                    .padding(MaterialTheme.spacing.medium)\n                    .fillMaxWidth()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/components/MazeItemButton.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.components\n\nimport androidx.compose.animation.core.animateFloatAsState\nimport androidx.compose.animation.core.animateIntAsState\nimport androidx.compose.foundation.LocalIndication\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.interaction.collectIsPressedAsState\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material.icons.rounded.Lock\nimport androidx.compose.material.icons.rounded.PlayArrow\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.draw.scale\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\n\n@Composable\ninternal fun MazeItemButton(\n    modifier: Modifier = Modifier,\n    itemPlayed: Boolean,\n    isPlayableItem: Boolean,\n    colors: MazeColors = MazeDefaults.defaultColors(),\n) {\n    val canPress = isPlayableItem || itemPlayed\n\n    val interactionSource = remember { MutableInteractionSource() }\n    val isPressed by interactionSource.collectIsPressedAsState()\n\n    val scale by animateFloatAsState(\n        if (isPressed) CirclePressScale else 1f,\n        label = \"Scale\"\n    )\n    val circleRadius by animateIntAsState(\n        if (isPressed) CirclePressedRadius else CircleUnPressedRadius,\n        label = \"Circle Radius\"\n    )\n\n    Box(\n        modifier = modifier\n            .size(CircleSize)\n            .scale(scale)\n            .background(\n                color = colors.circleContainerColor(\n                    played = itemPlayed,\n                    isPlayItem = isPlayableItem\n                ),\n                shape = RoundedCornerShape(circleRadius)\n            )\n            .then(\n                if (isPlayableItem) {\n                    Modifier\n                        .padding(CurrentCircleInnerPadding)\n                        .background(\n                            color = colors.currentCircleInnerColor(),\n                            shape = CircleShape\n                        )\n                } else {\n                    Modifier\n                }\n            )\n            .clip(CircleShape)\n            .clickable(\n                interactionSource = interactionSource,\n                indication = LocalIndication.current,\n                onClick = {},\n                enabled = canPress,\n                role = Role.Button\n            ),\n        contentAlignment = Alignment.Center\n    ) {\n        Icon(\n            imageVector = when {\n                !isPlayableItem && !itemPlayed -> Icons.Rounded.Lock\n                !isPlayableItem && itemPlayed -> Icons.Rounded.Check\n                else -> Icons.Rounded.PlayArrow\n            },\n            contentDescription = null,\n            tint = colors.circleContentColor(\n                played = itemPlayed,\n                isPlayItem = isPlayableItem\n            ),\n            modifier = Modifier.size(CircleSize / 2)\n        )\n    }\n}\n\nprivate val CircleSize = 50.dp\nprivate val CurrentCircleInnerPadding = 6.dp\n\nprivate const val CirclePressScale = 1.1f\n\nprivate const val CirclePressedRadius = 20\nprivate const val CircleUnPressedRadius = 50\n\n@Composable\n@PreviewLightDark\nprivate fun MazeItemButtonPreview() {\n    NewQuizTheme {\n        Surface {\n            Column(\n                modifier = Modifier.padding(16.dp),\n                horizontalAlignment = Alignment.CenterHorizontally,\n                verticalArrangement = Arrangement.spacedBy(16.dp)\n            ) {\n                MazeItemButton(\n                    itemPlayed = true,\n                    isPlayableItem = true,\n                )\n                MazeItemButton(\n                    itemPlayed = false,\n                    isPlayableItem = true,\n                )\n                MazeItemButton(\n                    itemPlayed = false,\n                    isPlayableItem = false,\n                )\n                MazeItemButton(\n                    itemPlayed = true,\n                    isPlayableItem = false,\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/components/MazePath.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.components\n\nimport androidx.compose.animation.core.Spring\nimport androidx.compose.animation.core.animateFloatAsState\nimport androidx.compose.animation.core.spring\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.foundation.gestures.detectTapGestures\nimport androidx.compose.foundation.gestures.detectVerticalDragGestures\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxWithConstraints\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material.icons.rounded.Lock\nimport androidx.compose.material.icons.rounded.PlayArrow\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableFloatStateOf\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Size\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Path\nimport androidx.compose.ui.graphics.PathEffect\nimport androidx.compose.ui.graphics.StrokeCap\nimport androidx.compose.ui.graphics.asComposePath\nimport androidx.compose.ui.graphics.drawscope.DrawScope\nimport androidx.compose.ui.graphics.drawscope.Stroke\nimport androidx.compose.ui.graphics.drawscope.scale\nimport androidx.compose.ui.graphics.drawscope.translate\nimport androidx.compose.ui.graphics.vector.VectorPainter\nimport androidx.compose.ui.graphics.vector.rememberVectorPainter\nimport androidx.compose.ui.input.pointer.pointerInput\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.zIndex\nimport androidx.graphics.shapes.CornerRounding\nimport androidx.graphics.shapes.RoundedPolygon\nimport androidx.graphics.shapes.toPath\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.util.collections.indexOfFirstOrNull\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport com.infinitepower.newquiz.model.maze.isItemPlayed\nimport com.infinitepower.newquiz.model.maze.isPlayableItem\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.toPersistentList\nimport kotlin.math.PI\nimport kotlin.math.pow\nimport kotlin.math.sin\nimport kotlin.random.Random\n\n@Composable\nfun MazePath(\n    modifier: Modifier = Modifier,\n    items: ImmutableList<MazeQuiz.MazeItem>,\n    startScrollToCurrentItem: Boolean = true,\n    colors: MazeColors = MazeDefaults.defaultColors(),\n    horizontalPadding: Dp = MazeDefaults.horizontalPadding,\n    verticalPadding: Dp = MazeDefaults.verticalPadding,\n    mazeSeed: Int = 0,\n    onItemClick: (item: MazeQuiz.MazeItem) -> Unit = {},\n) {\n    val random = remember(mazeSeed) { Random(mazeSeed) }\n\n    val localDensity = LocalDensity.current\n    val horizontalPaddingPx = with(localDensity) { horizontalPadding.toPx() }\n    val verticalPaddingPx = with(localDensity) { verticalPadding.toPx() }\n\n    val yPointsSize = items.size\n\n    // Find the index of the current play item.\n    val currentPlayItemIndex = remember(items) {\n        items.indexOfFirstOrNull { !it.played }\n    }\n\n    BoxWithConstraints(modifier = modifier) {\n        val screenHeight = constraints.maxHeight\n        val screenWidth = constraints.maxWidth\n\n        val points: List<Offset> = remember(yPointsSize) {\n            List(yPointsSize) { i ->\n                // Get a random number between 2 and 5 to make the horizontal offset of the points more random\n                val r = random.nextDouble(\n                    from = HorizontalOffsetRandomStart,\n                    until = HorizontalOffsetRandomEnd\n                ).toFloat()\n\n                val horizontalWidth = screenWidth - horizontalPaddingPx\n                val x = sin((i.toFloat() / 2) * PI) * horizontalWidth / r + screenWidth / 2\n                val y = localDensity.getPointY(\n                    height = screenHeight.toFloat(),\n                    index = i,\n                    verticalPaddingPx = verticalPaddingPx,\n                )\n\n                Offset(x.toFloat(), y)\n            }\n        }\n\n        // Calculate the height of the graph based on the number of points.\n        val graphHeight = remember(yPointsSize) {\n            with(localDensity) { PointSpacing.toPx() * (yPointsSize - 1) } + 2 * verticalPaddingPx\n        }\n\n        var topScroll by remember { mutableFloatStateOf(0f) }\n        val topScrollAnimated = animateFloatAsState(\n            targetValue = topScroll,\n            animationSpec = spring(\n                dampingRatio = Spring.DampingRatioLowBouncy,\n                stiffness = Spring.StiffnessLow\n            ),\n            label = \"Top Scroll\"\n        )\n\n        val playPainter = rememberVectorPainter(image = Icons.Rounded.PlayArrow)\n        val playedPainter = rememberVectorPainter(image = Icons.Rounded.Check)\n        val lockPainter = rememberVectorPainter(image = Icons.Rounded.Lock)\n\n        var pressedOffset by remember { mutableStateOf(Offset.Zero) }\n\n        val scrollToCurrentItem: () -> Unit = {\n            currentPlayItemIndex?.let { index ->\n                // Get the current play item's y position.\n                val currentPlayItemY = points[index].y\n                // Get the height of the screen divided by 2.\n                // This is used to center the current play item on the screen.\n                val halfScreenHeight = screenHeight / 2\n\n                val newTopScroll = -currentPlayItemY + halfScreenHeight\n\n                // If the top scroll is less than 0, it means that the current play item is\n                // not above the screen. In this case, we don't need to scroll.\n                if (newTopScroll >= 0) {\n                    val newGraphHeight = graphHeight - newTopScroll\n\n                    topScroll = if (newGraphHeight >= screenHeight) {\n                        newTopScroll\n                    } else {\n                        graphHeight - screenHeight\n                    }\n                } else {\n                    topScroll = 0f\n                }\n            }\n        }\n\n        val visibleScreenHeightRange = remember(topScroll, graphHeight) {\n            graphHeight - topScroll - screenHeight..graphHeight - topScroll\n        }\n\n        // The scroll button is visible if the current play item is not visible in the screen.\n        val scrollButtonState = remember(currentPlayItemIndex, visibleScreenHeightRange) {\n            if (currentPlayItemIndex == null) {\n                ScrollButtonState.HIDDEN\n            } else {\n                val currentPlayItemY = points[currentPlayItemIndex].y + graphHeight - screenHeight\n                if (currentPlayItemY !in visibleScreenHeightRange) {\n                    if (currentPlayItemY < visibleScreenHeightRange.start) {\n                        ScrollButtonState.SCROLL_TO_TOP\n                    } else {\n                        ScrollButtonState.SCROLL_TO_BOTTOM\n                    }\n                } else {\n                    ScrollButtonState.HIDDEN\n                }\n            }\n        }\n\n        ScrollToCurrentQuestionButton(\n            modifier = Modifier\n                .zIndex(1f)\n                .align(Alignment.BottomEnd)\n                .padding(MaterialTheme.spacing.medium),\n            state = scrollButtonState,\n            onClick = scrollToCurrentItem\n        )\n\n        // If the startScrollToCurrentItem is true, scroll to the current item.\n        LaunchedEffect(key1 = Unit) {\n            if (startScrollToCurrentItem) {\n                scrollToCurrentItem()\n            }\n        }\n\n        Canvas(\n            modifier = Modifier\n                .fillMaxWidth()\n                .height(graphHeight.dp)\n                .pointerInput(Unit) {\n                    detectVerticalDragGestures { _, dragAmount ->\n                        val newTopScroll = topScroll + dragAmount\n                        val newGraphHeight = graphHeight - newTopScroll\n\n                        if (newTopScroll >= 0 && newGraphHeight >= screenHeight) {\n                            topScroll += dragAmount\n                        }\n                    }\n                }\n                .pointerInput(currentPlayItemIndex) {\n                    detectTapGestures(\n                        onTap = { tapOffset ->\n                            // Find the index of the item that was tapped\n                            val tapIndex = points.indexOfFirstOrNull { point ->\n                                val realMazePoint = point.copy(y = point.y + topScroll)\n                                tapOffset.isInsideCircle(realMazePoint, CircleSize.toPx())\n                            }\n\n                            if (tapIndex != null && tapIndex in items.indices) {\n                                if (items.isPlayableItem(tapIndex) || items.isItemPlayed(tapIndex)) {\n                                    onItemClick(items[tapIndex])\n                                }\n                            }\n                        },\n                        onPress = { pressOffset ->\n                            pressedOffset = pressOffset.copy(\n                                y = pressOffset.y - topScroll\n                            )\n                            awaitRelease()\n                            pressedOffset = Offset.Zero\n                        }\n                    )\n                }\n        ) {\n            val completedPath = Path()\n            val remainingPath = Path()\n\n            points.forEachIndexed { i, point ->\n                val currentY = point.y\n\n                val path =\n                    if (items.isItemPlayed(i) || items.isPlayableItem(i)) completedPath else remainingPath\n\n                val isPlayItem = items.isPlayableItem(i - 1)\n\n                val previousY = getPointY(\n                    height = size.height,\n                    index = i - 1,\n                    verticalPaddingPx = verticalPaddingPx,\n                )\n\n                if (i == 0) {\n                    path.moveTo(size.width / 2, currentY)\n                } else {\n                    if (isPlayItem) {\n                        path.moveTo(\n                            x = points[i - 1].x,\n                            y = previousY\n                        )\n                    }\n\n                    val conY1 = PathSmoothness * previousY + (1 - PathSmoothness) * currentY\n                    val conY2 = PathSmoothness * currentY + (1 - PathSmoothness) * previousY\n\n                    val conX1 = points[i - 1].x\n                    val conX2 = points[i].x\n\n                    path.cubicTo(\n                        x1 = conX1,\n                        y1 = conY1,\n                        x2 = conX2,\n                        y2 = conY2,\n                        x3 = points[i].x,\n                        y3 = currentY\n                    )\n                }\n            }\n\n            translate(top = topScrollAnimated.value) {\n                // Draw the path for the completed items\n                drawMazePath(\n                    path = completedPath,\n                    color = colors.pathColor(played = true),\n                )\n\n                // Draw the path for the remaining items\n                drawMazePath(\n                    path = remainingPath,\n                    color = colors.pathColor(played = false),\n                )\n\n                // Draw the points\n                drawPoints(\n                    points = points,\n                    items = items,\n                    colors = colors,\n                    pressedOffset = pressedOffset,\n                    lockPainter = lockPainter,\n                    playPainter = playPainter,\n                    playedPainter = playedPainter,\n                )\n            }\n        }\n    }\n}\n\nprivate fun DrawScope.drawMazePath(\n    path: Path,\n    color: Color,\n) {\n    drawPath(\n        path = path,\n        color = color,\n        style = Stroke(\n            width = LineSize.toPx(),\n            cap = StrokeCap.Round,\n            pathEffect = PathEffect.dashPathEffect(MazePathEffectInterval)\n        ),\n    )\n}\n\nprivate fun DrawScope.drawPoints(\n    points: List<Offset>,\n    items: ImmutableList<MazeQuiz.MazeItem>,\n    colors: MazeColors,\n    pressedOffset: Offset,\n    lockPainter: VectorPainter,\n    playPainter: VectorPainter,\n    playedPainter: VectorPainter,\n) {\n    points.forEachIndexed { itemIndex, pointOffset ->\n        val itemPlayed = items.isItemPlayed(itemIndex)\n        val isPlayableItem = items.isPlayableItem(itemIndex)\n\n        val canPress = isPlayableItem || itemPlayed\n        val isPressed = canPress && pressedOffset.isInsideCircle(\n            pointOffset,\n            CircleSize.toPx()\n        )\n\n        scale(\n            scale = if (isPressed) CirclePressScale else 1f,\n            pivot = pointOffset\n        ) {\n            val currentItemRoundedPolygon = RoundedPolygon(\n                numVertices = ITEM_POLYGON_NUM_VERTICES,\n                radius = CircleSize.toPx(),\n                centerX = pointOffset.x,\n                centerY = pointOffset.y,\n                rounding = CornerRounding(\n                    radius = size.minDimension / ITEM_POLYGON_RADIUS_PERCENTAGE,\n                    smoothing = 0.1f\n                )\n            )\n            val roundedPolygonPath = currentItemRoundedPolygon.toPath().asComposePath()\n\n            drawPath(\n                path = roundedPolygonPath,\n                color = colors.circleContainerColor(\n                    played = itemPlayed,\n                    isPlayItem = isPlayableItem\n                ),\n            )\n\n            if (isPlayableItem) {\n                drawCircle(\n                    color = colors.currentCircleInnerColor(),\n                    radius = CircleInnerSize.toPx(),\n                    center = pointOffset\n                )\n            }\n\n            translate(\n                left = pointOffset.x - IconSize.toPx() / 2,\n                top = pointOffset.y - IconSize.toPx() / 2\n            ) {\n                with(\n                    when {\n                        !isPlayableItem && !itemPlayed -> lockPainter // Locked\n                        !isPlayableItem && itemPlayed -> playedPainter // Played\n                        else -> playPainter\n                    }\n                ) {\n                    draw(\n                        size = Size(IconSize.toPx(), IconSize.toPx()),\n                        colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(\n                            colors.circleContentColor(\n                                played = itemPlayed,\n                                isPlayItem = isPlayableItem\n                            )\n                        )\n                    )\n                }\n            }\n        }\n    }\n}\n\n/**\n * Returns true if the [Offset] is inside the circle with the given [center] and [radius].\n */\nprivate fun Offset.isInsideCircle(\n    center: Offset,\n    radius: Float\n): Boolean {\n    val dx = x - center.x\n    val dy = y - center.y\n    return dx.pow(2) + dy.pow(2) <= radius.pow(2)\n}\n\nprivate fun Density.getPointY(\n    height: Float,\n    index: Int,\n    verticalPaddingPx: Float,\n): Float {\n    return height - index * PointSpacing.toPx() - verticalPaddingPx\n}\n\nobject MazeDefaults {\n    /**\n     * The horizontal padding applied to the graph.\n     */\n    val horizontalPadding: Dp @Composable get() = 120.dp\n\n    /**\n     * The vertical padding applied to the graph.\n     */\n    val verticalPadding: Dp @Composable get() = MaterialTheme.spacing.extraLarge\n\n    @Composable\n    fun defaultColors(\n        playedPathColor: Color = MaterialTheme.colorScheme.primary,\n        playedCircleContainerColor: Color = MaterialTheme.colorScheme.primary,\n        playedCircleContentColor: Color = MaterialTheme.colorScheme.onPrimary,\n        lockedPathColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.75f),\n        lockedCircleContainerColor: Color = MaterialTheme.colorScheme.surfaceVariant,\n        lockedCircleContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.38f),\n        currentCircleContainerColor: Color = playedCircleContainerColor,\n        currentCircleInnerColor: Color = MaterialTheme.colorScheme.surface,\n        currentCircleContentColor: Color = currentCircleContainerColor,\n    ): MazeColors = MazeColors(\n        playedPathColor = playedPathColor,\n        playedCircleContainerColor = playedCircleContainerColor,\n        playedCircleContentColor = playedCircleContentColor,\n        lockedPathColor = lockedPathColor,\n        lockedCircleContainerColor = lockedCircleContainerColor,\n        lockedCircleContentColor = lockedCircleContentColor,\n        currentCircleContainerColor = currentCircleContainerColor,\n        currentCircleInnerColor = currentCircleInnerColor,\n        currentCircleContentColor = currentCircleContentColor,\n    )\n}\n\n@Immutable\nclass MazeColors internal constructor(\n    private val playedPathColor: Color,\n    private val playedCircleContainerColor: Color,\n    private val playedCircleContentColor: Color,\n    private val lockedPathColor: Color,\n    private val lockedCircleContainerColor: Color,\n    private val lockedCircleContentColor: Color,\n    private val currentCircleInnerColor: Color,\n    private val currentCircleContainerColor: Color,\n    private val currentCircleContentColor: Color,\n) {\n    internal fun pathColor(played: Boolean): Color {\n        return if (played) playedPathColor else lockedPathColor\n    }\n\n    internal fun circleContainerColor(\n        played: Boolean,\n        isPlayItem: Boolean,\n    ): Color {\n        return when {\n            !isPlayItem && !played -> lockedCircleContainerColor\n            !isPlayItem && played -> playedCircleContainerColor\n            else -> currentCircleContainerColor\n        }\n    }\n\n    internal fun circleContentColor(\n        played: Boolean,\n        isPlayItem: Boolean,\n    ): Color = when {\n        !isPlayItem && !played -> lockedCircleContentColor\n        !isPlayItem && played -> playedCircleContentColor\n        else -> currentCircleContentColor\n    }\n\n    internal fun currentCircleInnerColor(): Color {\n        return currentCircleInnerColor\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (other !is MazeColors) return false\n\n        if (playedPathColor != other.playedPathColor) return false\n        if (lockedPathColor != other.lockedPathColor) return false\n        if (playedCircleContainerColor != other.playedCircleContainerColor) return false\n        if (lockedCircleContainerColor != other.lockedCircleContainerColor) return false\n        if (playedCircleContentColor != other.playedCircleContentColor) return false\n        if (lockedCircleContentColor != other.lockedCircleContentColor) return false\n\n        return true\n    }\n\n    override fun hashCode(): Int {\n        var result = playedPathColor.hashCode()\n        result = 31 * result + lockedPathColor.hashCode()\n        result = 31 * result + playedCircleContainerColor.hashCode()\n        result = 31 * result + lockedCircleContainerColor.hashCode()\n        result = 31 * result + playedCircleContentColor.hashCode()\n        result = 31 * result + lockedCircleContentColor.hashCode()\n        return result\n    }\n}\n\nprivate val PointSpacing = 100.dp\nprivate val CircleSize = 35.dp\nprivate const val CIRCLE_INNER_SIZE_PERCENTAGE = 0.6f\nprivate val CircleInnerSize = CircleSize * CIRCLE_INNER_SIZE_PERCENTAGE\nprivate val IconSize = 30.dp\nprivate val LineSize = 12.dp\n\n/**\n * The smoothness of the path.\n */\nprivate const val PathSmoothness = 0.25f\n\nprivate const val CirclePressScale = 1.1f\n\nprivate const val ITEM_POLYGON_NUM_VERTICES = 6\nprivate const val ITEM_POLYGON_RADIUS_PERCENTAGE = 30f\n\nprivate const val MazePathEffectWidth = 50f\nprivate const val MazePathEffectSpacing = 50f\nprivate val MazePathEffectInterval = floatArrayOf(MazePathEffectWidth, MazePathEffectSpacing)\n\n/**\n * The range of the horizontal offset of the points.\n */\nprivate const val HorizontalOffsetRandomStart = 2.0\nprivate const val HorizontalOffsetRandomEnd = 5.0\n\n@Composable\n@PreviewLightDark\nprivate fun MazeComponentPreview() {\n    val completedItems = List(4) {\n        MazeQuiz.MazeItem.Wordle(\n            wordleWord = WordleWord(\"1+1=2\"),\n            difficulty = QuestionDifficulty.Easy,\n            played = true,\n            wordleQuizType = WordleQuizType.MATH_FORMULA,\n            mazeSeed = 0\n        )\n    }\n\n    val otherItems = List(8) {\n        MazeQuiz.MazeItem.Wordle(\n            wordleWord = WordleWord(\"1+1=2\"),\n            difficulty = QuestionDifficulty.Easy,\n            wordleQuizType = WordleQuizType.MATH_FORMULA,\n            mazeSeed = 0\n        )\n    }\n\n    val items = (completedItems + otherItems).toPersistentList()\n\n    NewQuizTheme {\n        Surface {\n            Box(\n                modifier = Modifier\n                    .fillMaxWidth()\n            ) {\n                MazePath(\n                    items = items,\n                    startScrollToCurrentItem = false,\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/components/ScrollToCurrentQuestionButton.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.components\n\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.ArrowDownward\nimport androidx.compose.material.icons.rounded.ArrowUpward\nimport androidx.compose.material3.FloatingActionButton\nimport androidx.compose.material3.Icon\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\n\n@Composable\ninternal fun ScrollToCurrentQuestionButton(\n    modifier: Modifier = Modifier,\n    state: ScrollButtonState = ScrollButtonState.SCROLL_TO_BOTTOM,\n    onClick: () -> Unit = {}\n) {\n    if (state != ScrollButtonState.HIDDEN) {\n        FloatingActionButton(\n            modifier = modifier,\n            onClick = onClick\n        ) {\n            val icon = if (state == ScrollButtonState.SCROLL_TO_TOP) {\n                Icons.Rounded.ArrowUpward\n            } else {\n                Icons.Rounded.ArrowDownward\n            }\n\n            Icon(\n                imageVector = icon,\n                contentDescription = \"Scroll to current question\"\n            )\n        }\n    }\n}\n\nenum class ScrollButtonState {\n    HIDDEN,\n    SCROLL_TO_TOP,\n    SCROLL_TO_BOTTOM\n}\n\n@Composable\n@PreviewLightDark\nprivate fun ScrollToCurrentQuestionButtonPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) scrollToTop: Boolean\n) {\n    NewQuizTheme {\n        ScrollToCurrentQuestionButton(\n            state = if (scrollToTop) ScrollButtonState.SCROLL_TO_TOP else ScrollButtonState.SCROLL_TO_BOTTOM\n        )\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/generate/GenerateMazeScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.generate\n\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.LazyListScope\nimport androidx.compose.foundation.lazy.LazyRow\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.SelectAll\nimport androidx.compose.material.icons.rounded.SignalWifiConnectedNoInternet4\nimport androidx.compose.material3.AssistChip\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.ExtendedFloatingActionButton\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.material3.TriStateCheckbox\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.derivedStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.state.ToggleableState\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.category.CategoryComponent\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.data.local.multi_choice_quiz.category.multiChoiceQuestionCategories\nimport com.infinitepower.newquiz.data.local.wordle.WordleCategories\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.toUiText\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.toImmutableList\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun GenerateMazeScreen(\n    onBackClick: () -> Unit,\n    viewModel: GenerateMazeScreenViewModel = hiltViewModel()\n) {\n    val uiState = viewModel.uiState.collectAsStateWithLifecycle()\n\n    GenerateMazeScreenImpl(\n        uiState = uiState.value,\n        onEvent = viewModel::onEvent,\n        onBackClick = onBackClick\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun GenerateMazeScreenImpl(\n    uiState: GenerateMazeScreenUiState,\n    onEvent: (event: GenerateMazeScreenUiEvent) -> Unit = {},\n    onBackClick: () -> Unit = {}\n) {\n    val showGenerateButton = remember(\n        uiState.selectedMultiChoiceCategories.size,\n        uiState.selectedWordleCategories.size,\n        uiState.selectedComparisonQuizCategories.size,\n        uiState.loading,\n        uiState.generatingMaze\n    ) {\n        derivedStateOf {\n            val anySelectCategory = uiState.selectedMultiChoiceCategories.isNotEmpty() ||\n                    uiState.selectedWordleCategories.isNotEmpty() ||\n                    uiState.selectedComparisonQuizCategories.isNotEmpty()\n\n            !uiState.loading && !uiState.generatingMaze && anySelectCategory\n        }\n    }\n\n    Scaffold(\n        topBar = {\n            TopAppBar(\n                title = { Text(text = stringResource(id = CoreR.string.generate_maze)) },\n                navigationIcon = { BackIconButton(onClick = onBackClick) }\n            )\n        },\n        floatingActionButton = {\n            if (showGenerateButton.value) {\n                ExtendedFloatingActionButton(\n                    onClick = {\n                        onEvent(GenerateMazeScreenUiEvent.GenerateMaze(seed = null))\n                    },\n                ) {\n                    Text(text = stringResource(id = CoreR.string.generate))\n                }\n            }\n        }\n    ) { innerPadding ->\n        Column(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(innerPadding),\n        ) {\n            if (uiState.loading || uiState.generatingMaze) {\n                CircularProgressIndicator()\n            } else {\n                HelperChipsRow(\n                    onSelectAllClick = {\n                        onEvent(GenerateMazeScreenUiEvent.SelectAllCategories)\n                    },\n                    onOnlyOfflineClick = {\n                        onEvent(GenerateMazeScreenUiEvent.SelectOnlyOfflineCategories)\n                    }\n                )\n                CategoriesContent(onEvent = onEvent, uiState = uiState)\n            }\n        }\n    }\n}\n\n@OptIn(ExperimentalFoundationApi::class)\n@Composable\nprivate fun CategoriesContent(\n    onEvent: (event: GenerateMazeScreenUiEvent) -> Unit,\n    uiState: GenerateMazeScreenUiState,\n) {\n    val multiChoiceParentBoxState = rememberParentBoxState(\n        selectedCategories = uiState.selectedMultiChoiceCategories,\n        categories = uiState.multiChoiceCategories\n    )\n    val wordleParentBoxState = rememberParentBoxState(\n        selectedCategories = uiState.selectedWordleCategories,\n        categories = uiState.wordleCategories\n    )\n    val comparisonQuizParentBoxState = rememberParentBoxState(\n        selectedCategories = uiState.selectedComparisonQuizCategories,\n        categories = uiState.comparisonQuizCategories\n    )\n\n    val multiChoiceHeader = stringResource(id = CoreR.string.multi_choice_quiz)\n    val wordleHeader = stringResource(id = CoreR.string.wordle)\n    val comparisonQuizHeader = stringResource(id = CoreR.string.comparison_quiz)\n\n    LazyColumn(\n        contentPadding = PaddingValues(vertical = MaterialTheme.spacing.medium),\n        verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.medium)\n    ) {\n        // Multi choice\n        categoriesStickyHeader(\n            title = multiChoiceHeader,\n            parentBoxState = multiChoiceParentBoxState,\n            onSelectAllClick = { selectAll ->\n                onEvent(\n                    GenerateMazeScreenUiEvent.SelectCategories(\n                        gameMode = GameMode.MULTI_CHOICE,\n                        selectAll = selectAll\n                    )\n                )\n            }\n        )\n        categoriesItems(\n            categories = uiState.multiChoiceCategories,\n            selectedCategories = uiState.selectedMultiChoiceCategories,\n            baseItemKey = \"multi-choice\",\n            onSelectClick = { category ->\n                onEvent(GenerateMazeScreenUiEvent.SelectCategory(category = category))\n            }\n        )\n\n        // Wordle\n        categoriesStickyHeader(\n            title = wordleHeader,\n            parentBoxState = wordleParentBoxState,\n            onSelectAllClick = { selectAll ->\n                onEvent(\n                    GenerateMazeScreenUiEvent.SelectCategories(\n                        gameMode = GameMode.WORDLE,\n                        selectAll = selectAll\n                    )\n                )\n            }\n        )\n        categoriesItems(\n            categories = uiState.wordleCategories,\n            selectedCategories = uiState.selectedWordleCategories,\n            baseItemKey = \"wordle\",\n            onSelectClick = { category ->\n                onEvent(GenerateMazeScreenUiEvent.SelectCategory(category = category))\n            }\n        )\n\n        // Comparison quiz\n        categoriesStickyHeader(\n            title = comparisonQuizHeader,\n            parentBoxState = comparisonQuizParentBoxState,\n            onSelectAllClick = { selectAll ->\n                onEvent(\n                    GenerateMazeScreenUiEvent.SelectCategories(\n                        gameMode = GameMode.COMPARISON_QUIZ,\n                        selectAll = selectAll\n                    )\n                )\n            }\n        )\n        categoriesItems(\n            categories = uiState.comparisonQuizCategories,\n            selectedCategories = uiState.selectedComparisonQuizCategories,\n            baseItemKey = \"comparison-quiz\",\n            onSelectClick = { category ->\n                onEvent(GenerateMazeScreenUiEvent.SelectCategory(category = category))\n            }\n        )\n    }\n}\n\n@Composable\nprivate fun <T : BaseCategory> rememberParentBoxState(\n    selectedCategories: ImmutableList<T>,\n    categories: ImmutableList<T>\n): ToggleableState = remember(\n    key1 = selectedCategories.size,\n    key2 = categories.size\n) {\n    if (selectedCategories.isEmpty()) {\n        ToggleableState.Off\n    } else if (selectedCategories.size == categories.size) {\n        ToggleableState.On\n    } else {\n        ToggleableState.Indeterminate\n    }\n}\n\n/**\n * Creates a sticky header for the categories list with a parent checkbox.\n *\n * @param title the title of the sticky header\n * @param parentBoxState the state of the parent checkbox\n * @param onSelectAllClick called when the parent checkbox is clicked\n */\n@ExperimentalFoundationApi\nprivate fun LazyListScope.categoriesStickyHeader(\n    title: String,\n    parentBoxState: ToggleableState,\n    onSelectAllClick: (selectAll: Boolean) -> Unit\n) {\n    stickyHeader {\n        Surface(\n            modifier = Modifier.fillParentMaxWidth(),\n            checked = parentBoxState != ToggleableState.Off,\n            onCheckedChange = onSelectAllClick,\n        ) {\n            Row(\n                modifier = Modifier\n                    .fillParentMaxWidth()\n                    .padding(\n                        horizontal = MaterialTheme.spacing.medium,\n                        vertical = MaterialTheme.spacing.small\n                    )\n                    .background(color = MaterialTheme.colorScheme.surface),\n                verticalAlignment = Alignment.CenterVertically,\n                horizontalArrangement = Arrangement.SpaceBetween\n            ) {\n                Text(\n                    text = title,\n                    style = MaterialTheme.typography.headlineMedium\n                )\n                TriStateCheckbox(\n                    state = parentBoxState,\n                    onClick = {\n                        val selectAll = parentBoxState == ToggleableState.Off\n                        onSelectAllClick(selectAll)\n                    },\n                )\n            }\n        }\n    }\n}\n\n/**\n * Creates a list of categories.\n *\n * @param categories the list of categories\n * @param selectedCategories the list of selected categories\n * @param baseItemKey the base key for the items, it is used to generate the key for each game mode.\n */\nprivate fun <T : BaseCategory> LazyListScope.categoriesItems(\n    categories: ImmutableList<T>,\n    selectedCategories: ImmutableList<T>,\n    baseItemKey: String = \"\",\n    onSelectClick: (category: T) -> Unit,\n) {\n    items(\n        items = categories,\n        key = { category -> \"$baseItemKey-${category.id}\" }\n    ) { category ->\n        CategoryComponent(\n            title = category.name.asString(),\n            imageUrl = category.image,\n            onClick = { onSelectClick(category) },\n            onCheckClick = { onSelectClick(category) },\n            requireInternetConnection = category.requireInternetConnection,\n            showConnectionInfo = ShowCategoryConnectionInfo.BOTH,\n            modifier = Modifier\n                .fillParentMaxWidth()\n                .padding(horizontal = MaterialTheme.spacing.medium),\n            checked = category in selectedCategories,\n        )\n    }\n}\n\n@Composable\nprivate fun HelperChipsRow(\n    onSelectAllClick: () -> Unit,\n    onOnlyOfflineClick: () -> Unit,\n    modifier: Modifier = Modifier\n) {\n    LazyRow(\n        modifier = modifier,\n        horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.medium),\n        contentPadding = PaddingValues(horizontal = MaterialTheme.spacing.medium)\n    ) {\n        item {\n            AssistChip(\n                onClick = onSelectAllClick,\n                label = {\n                    Text(text = stringResource(id = CoreR.string.select_all))\n                },\n                leadingIcon = {\n                    Icon(\n                        imageVector = Icons.Rounded.SelectAll,\n                        contentDescription = null\n                    )\n                },\n            )\n        }\n\n        item {\n            AssistChip(\n                onClick = onOnlyOfflineClick,\n                label = {\n                    Text(text = stringResource(id = CoreR.string.select_only_offline))\n                },\n                leadingIcon = {\n                    Icon(\n                        imageVector = Icons.Rounded.SignalWifiConnectedNoInternet4,\n                        contentDescription = null\n                    )\n                },\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun MazeScreenPreview() {\n    val multiChoiceQuestionCategories = multiChoiceQuestionCategories.take(8).toImmutableList()\n    val selectedMultiChoiceCategories = multiChoiceQuestionCategories.take(2).toImmutableList()\n\n    val wordleCategories = WordleCategories.allCategories.toImmutableList()\n    val selectedWordleCategories = wordleCategories.take(2).toImmutableList()\n\n    val comparisonQuizCategories = List(5) {\n        ComparisonQuizCategory(\n            id = it.toString(),\n            name = \"Category $it\".toUiText(),\n            image = \"\",\n            description = \"\",\n            questionDescription = ComparisonQuizCategory.QuestionDescription(\n                greater = \"greater\",\n                less = \"less\"\n            ),\n            formatType = NumberFormatType.DEFAULT,\n        )\n    }.toImmutableList()\n\n    NewQuizTheme {\n        Surface {\n            GenerateMazeScreenImpl(\n                uiState = GenerateMazeScreenUiState(\n                    multiChoiceCategories = multiChoiceQuestionCategories,\n                    selectedMultiChoiceCategories = selectedMultiChoiceCategories,\n                    wordleCategories = wordleCategories,\n                    selectedWordleCategories = selectedWordleCategories,\n                    comparisonQuizCategories = comparisonQuizCategories,\n                    loading = false\n                )\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/generate/GenerateMazeScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.generate\n\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\n\nsealed interface GenerateMazeScreenUiEvent {\n    data class GenerateMaze(\n        val seed: Int?\n    ) : GenerateMazeScreenUiEvent\n\n    data object SelectAllCategories : GenerateMazeScreenUiEvent\n\n    data object SelectOnlyOfflineCategories : GenerateMazeScreenUiEvent\n\n    data class SelectCategories(\n        val gameMode: GameMode,\n        val selectAll: Boolean,\n    ) : GenerateMazeScreenUiEvent\n\n    data class SelectCategory(\n        val category: BaseCategory\n    ) : GenerateMazeScreenUiEvent\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/generate/GenerateMazeScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.generate\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.BaseCategory\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\n\n@Keep\ndata class GenerateMazeScreenUiState(\n    val loading: Boolean = true,\n    val generatingMaze: Boolean = false,\n    val multiChoiceCategories: ImmutableList<BaseCategory> = persistentListOf(),\n    val selectedMultiChoiceCategories: ImmutableList<BaseCategory> = persistentListOf(),\n    val wordleCategories: ImmutableList<BaseCategory> = persistentListOf(),\n    val selectedWordleCategories: ImmutableList<BaseCategory> = persistentListOf(),\n    val comparisonQuizCategories: ImmutableList<BaseCategory> = persistentListOf(),\n    val selectedComparisonQuizCategories: ImmutableList<BaseCategory> = persistentListOf(),\n)\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/generate/GenerateMazeScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.generate\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.asFlow\nimport androidx.lifecycle.viewModelScope\nimport androidx.work.WorkInfo\nimport androidx.work.WorkManager\nimport com.infinitepower.newquiz.core.ui.SnackbarController\nimport com.infinitepower.newquiz.data.worker.maze.GenerateMazeQuizWorker\nimport com.infinitepower.newquiz.domain.repository.comparison_quiz.ComparisonQuizRepository\nimport com.infinitepower.newquiz.feature.maze.common.MazeCategories\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.collections.immutable.persistentListOf\nimport kotlinx.collections.immutable.toImmutableList\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\nclass GenerateMazeScreenViewModel @Inject constructor(\n    private val workManager: WorkManager,\n    private val comparisonQuizRepository: ComparisonQuizRepository\n) : ViewModel() {\n    private val _uiState = MutableStateFlow(GenerateMazeScreenUiState())\n    val uiState = _uiState.asStateFlow()\n\n    init {\n        viewModelScope.launch {\n            _uiState.update { currentState ->\n                currentState.copy(\n                    multiChoiceCategories = MazeCategories.getMultiChoiceCategories()\n                        .toImmutableList(),\n                    wordleCategories = MazeCategories.availableWordleCategories.toImmutableList(),\n                    comparisonQuizCategories = getComparisonQuizCategories().toImmutableList(),\n                    loading = false\n                )\n            }\n        }\n    }\n\n    fun onEvent(event: GenerateMazeScreenUiEvent) {\n        when (event) {\n            is GenerateMazeScreenUiEvent.SelectCategory -> selectCategory(event.category)\n            is GenerateMazeScreenUiEvent.GenerateMaze -> generateMaze(seed = event.seed)\n            is GenerateMazeScreenUiEvent.SelectAllCategories -> selectAllCategories()\n            is GenerateMazeScreenUiEvent.SelectOnlyOfflineCategories -> selectOnlyOffline()\n            is GenerateMazeScreenUiEvent.SelectCategories -> selectCategories(\n                gameMode = event.gameMode,\n                selectAll = event.selectAll\n            )\n        }\n    }\n\n    /**\n     * Select or deselect a category, depending on the current state.\n     */\n    private fun selectCategory(category: BaseCategory) {\n        _uiState.update { currentState ->\n            // Get the list of selected categories, depending on the game mode.\n            val selectedCategories = when (category.gameMode) {\n                GameMode.MULTI_CHOICE -> currentState.selectedMultiChoiceCategories\n                GameMode.WORDLE -> currentState.selectedWordleCategories\n                GameMode.COMPARISON_QUIZ -> currentState.selectedComparisonQuizCategories\n            }.toMutableList()\n\n            // If the category is already selected, then deselect it.\n            // If the category is not selected, then select it.\n            if (selectedCategories.contains(category)) {\n                selectedCategories.remove(category)\n            } else {\n                selectedCategories.add(category)\n            }\n\n            // Update the state, depending on the game mode.\n            when (category.gameMode) {\n                GameMode.MULTI_CHOICE -> currentState.copy(\n                    selectedMultiChoiceCategories = selectedCategories.toImmutableList()\n                )\n\n                GameMode.WORDLE -> currentState.copy(\n                    selectedWordleCategories = selectedCategories.toImmutableList()\n                )\n\n                GameMode.COMPARISON_QUIZ -> currentState.copy(\n                    selectedComparisonQuizCategories = selectedCategories.toImmutableList()\n                )\n            }\n        }\n    }\n\n    private fun selectAllCategories() {\n        selectMultiChoiceCategories(selectAll = true)\n        selectWordleCategories(selectAll = true)\n        selectComparisonQuizCategories(selectAll = true)\n    }\n\n    private fun selectOnlyOffline() {\n        _uiState.update { currentState ->\n            val multiChoiceOnlyOffline = currentState.multiChoiceCategories.filter { category ->\n                !category.requireInternetConnection\n            }\n\n            val wordleOnlyOffline = currentState.wordleCategories.filter { category ->\n                !category.requireInternetConnection\n            }\n\n            val comparisonQuizOnlyOffline =\n                currentState.comparisonQuizCategories.filter { category ->\n                    !category.requireInternetConnection\n                }\n\n            currentState.copy(\n                selectedMultiChoiceCategories = multiChoiceOnlyOffline.toImmutableList(),\n                selectedWordleCategories = wordleOnlyOffline.toImmutableList(),\n                selectedComparisonQuizCategories = comparisonQuizOnlyOffline.toImmutableList()\n            )\n        }\n    }\n\n    private fun selectCategories(\n        gameMode: GameMode,\n        selectAll: Boolean,\n    ) {\n        when (gameMode) {\n            GameMode.MULTI_CHOICE -> selectMultiChoiceCategories(selectAll)\n            GameMode.WORDLE -> selectWordleCategories(selectAll)\n            GameMode.COMPARISON_QUIZ -> selectComparisonQuizCategories(selectAll)\n        }\n    }\n\n    private fun selectMultiChoiceCategories(selectAll: Boolean) {\n        _uiState.update { currentState ->\n            if (selectAll) {\n                currentState.copy(selectedMultiChoiceCategories = currentState.multiChoiceCategories)\n            } else {\n                currentState.copy(selectedMultiChoiceCategories = persistentListOf())\n            }\n        }\n    }\n\n    private fun selectWordleCategories(selectAll: Boolean) {\n        _uiState.update { currentState ->\n            if (selectAll) {\n                currentState.copy(selectedWordleCategories = currentState.wordleCategories)\n            } else {\n                currentState.copy(selectedWordleCategories = persistentListOf())\n            }\n        }\n    }\n\n    private fun selectComparisonQuizCategories(selectAll: Boolean) {\n        _uiState.update { currentState ->\n            if (selectAll) {\n                currentState.copy(selectedComparisonQuizCategories = currentState.comparisonQuizCategories)\n            } else {\n                currentState.copy(selectedComparisonQuizCategories = persistentListOf())\n            }\n        }\n    }\n\n    private fun generateMaze(seed: Int?) {\n        viewModelScope.launch {\n            val currentState = uiState.first()\n\n            val workId = GenerateMazeQuizWorker.enqueue(\n                workManager = workManager,\n                seed = seed,\n                multiChoiceCategories = currentState.selectedMultiChoiceCategories,\n                wordleCategories = currentState.selectedWordleCategories,\n                comparisonQuizCategories = currentState.selectedComparisonQuizCategories\n            )\n\n            workManager\n                .getWorkInfoByIdLiveData(workId)\n                .asFlow()\n                .onEach { workInfo ->\n                    if (workInfo?.state == WorkInfo.State.FAILED) {\n                        SnackbarController.sendShortMessage(\"Failed to generate maze\")\n                    }\n\n                    _uiState.update { currentState ->\n                        val isFinished = workInfo?.state?.isFinished ?: false\n                        currentState.copy(generatingMaze = !isFinished)\n                    }\n                }.launchIn(viewModelScope)\n        }\n    }\n\n    private fun getComparisonQuizCategories(): List<BaseCategory> {\n        return comparisonQuizRepository.getCategories().filterNot { it.isMazeDisabled }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/level_results/LevelResultsScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.level_results\n\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.rememberUpdatedState\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.navigation.MazeNavigator\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.ui.SnackbarController\nimport com.infinitepower.newquiz.feature.maze.level_results.components.LevelCompletedContent\nimport com.infinitepower.newquiz.feature.maze.level_results.components.LevelFailedContent\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\n\n@Composable\n@Destination(navArgsDelegate = LevelResultsScreenArgs::class)\ninternal fun LevelResultsScreen(\n    navigator: DestinationsNavigator,\n    mazeNavigator: MazeNavigator,\n    viewModel: LevelResultsScreenViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    LevelResultsScreenImpl(\n        uiState = uiState,\n        popBackStack = navigator::popBackStack,\n        navigateToNextLevel = { uiState.nextAvailableItem?.let(mazeNavigator::navigateToGame) },\n        retryLevel = { uiState.currentItem?.let(mazeNavigator::navigateToGame) }\n    )\n}\n\n@Composable\nprivate fun LevelResultsScreenImpl(\n    popBackStack: () -> Unit,\n    navigateToNextLevel: () -> Unit,\n    retryLevel: () -> Unit,\n    uiState: LevelResultsScreenUiState\n) {\n    Scaffold(\n        modifier = Modifier.fillMaxWidth(),\n    ) { innerPadding ->\n        when {\n            uiState.loading -> CircularProgressIndicator()\n            !uiState.loading && uiState.error != null -> {\n                val currentPopBackStack by rememberUpdatedState(popBackStack)\n\n                LaunchedEffect(Unit) {\n                    SnackbarController.sendShortMessage(uiState.error)\n                    currentPopBackStack()\n                }\n            }\n\n            else -> {\n                if (uiState.completed) {\n                    LevelCompletedContent(\n                        modifier = Modifier.padding(innerPadding),\n                        onNext = navigateToNextLevel,\n                        popBackStack = popBackStack,\n                        nextLevelExists = uiState.nextAvailableItem != null\n                    )\n                } else {\n                    LevelFailedContent(\n                        onRetry = retryLevel,\n                        popBackStack = popBackStack,\n                        modifier = Modifier.padding(innerPadding)\n                    )\n                }\n            }\n        }\n    }\n}\n\ndata class LevelResultsScreenArgs(\n    val mazeItemId: Int\n)\n\n@Composable\n@PreviewLightDark\nprivate fun LevelResultsScreenPreview() {\n    NewQuizTheme {\n        LevelResultsScreenImpl(\n            uiState = LevelResultsScreenUiState(),\n            popBackStack = {},\n            navigateToNextLevel = {},\n            retryLevel = {}\n        )\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/level_results/LevelResultsScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.level_results\n\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\n\ndata class LevelResultsScreenUiState(\n    val loading: Boolean = true,\n    val completed: Boolean = false,\n    val currentItem: MazeQuiz.MazeItem? = null,\n    val nextAvailableItem: MazeQuiz.MazeItem? = null,\n    val error: String? = null\n)\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/level_results/LevelResultsScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.level_results\n\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.feature.maze.destinations.LevelResultsScreenDestination\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\nclass LevelResultsScreenViewModel @Inject constructor(\n    private val mazeQuizRepository: MazeQuizRepository,\n    savedStateHandle: SavedStateHandle\n) : ViewModel() {\n    private val navArgs = LevelResultsScreenDestination.argsFrom(savedStateHandle)\n\n    private val _uiState = MutableStateFlow(LevelResultsScreenUiState())\n    val uiState = _uiState.asStateFlow()\n\n    init {\n        viewModelScope.launch {\n            val item = mazeQuizRepository.getMazeItemById(navArgs.mazeItemId)\n\n            if (item == null) {\n                _uiState.update { currentState ->\n                    currentState.copy(\n                        loading = false,\n                        error = \"Item not found\"\n                    )\n                }\n\n                return@launch\n            }\n\n            val nextAvailableLevel = mazeQuizRepository.getNextAvailableMazeItem()\n\n            _uiState.update { currentState ->\n                currentState.copy(\n                    loading = false,\n                    completed = item.played,\n                    currentItem = item,\n                    nextAvailableItem = nextAvailableLevel\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/level_results/components/LevelCompletedContent.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.level_results.components\n\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.automirrored.rounded.ArrowForward\nimport androidx.compose.material.icons.rounded.Home\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.toArgb\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.airbnb.lottie.LottieProperty\nimport com.airbnb.lottie.SimpleColorFilter\nimport com.airbnb.lottie.compose.LottieAnimation\nimport com.airbnb.lottie.compose.LottieCompositionSpec\nimport com.airbnb.lottie.compose.rememberLottieComposition\nimport com.airbnb.lottie.compose.rememberLottieDynamicProperties\nimport com.airbnb.lottie.compose.rememberLottieDynamicProperty\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun LevelCompletedContent(\n    onNext: () -> Unit,\n    popBackStack: () -> Unit,\n    modifier: Modifier = Modifier,\n    nextLevelExists: Boolean = true\n) {\n    Column(\n        modifier = modifier\n            .padding(MaterialTheme.spacing.large)\n            .fillMaxWidth(),\n        horizontalAlignment = Alignment.CenterHorizontally\n    ) {\n        Text(\n            text = stringResource(id = CoreR.string.level_completed),\n            style = MaterialTheme.typography.headlineLarge\n        )\n        TrophyIcon(modifier = Modifier.padding(vertical = MaterialTheme.spacing.large))\n        if (nextLevelExists) {\n            Button(\n                onClick = onNext,\n                modifier = Modifier.fillMaxWidth()\n            ) {\n                Text(text = stringResource(id = CoreR.string.next_level))\n                Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))\n                Icon(\n                    imageVector = Icons.AutoMirrored.Rounded.ArrowForward,\n                    contentDescription = null,\n                    modifier = Modifier.size(ButtonDefaults.IconSize)\n                )\n            }\n        }\n        TextButton(\n            onClick = popBackStack,\n            modifier = Modifier.fillMaxWidth(),\n        ) {\n            Icon(\n                imageVector = Icons.Rounded.Home,\n                contentDescription = null,\n                modifier = Modifier.size(ButtonDefaults.IconSize)\n            )\n            Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))\n            Text(text = stringResource(id = CoreR.string.main_menu))\n        }\n    }\n}\n\n@Composable\nprivate fun TrophyIcon(modifier: Modifier = Modifier) {\n    val trophySpec = LottieCompositionSpec.RawRes(CoreR.raw.trophy2)\n    val trophyLottieComposition by rememberLottieComposition(spec = trophySpec)\n\n    val dynamicProperties = rememberLottieDynamicProperties(\n        rememberLottieDynamicProperty(\n            property = LottieProperty.COLOR_FILTER,\n            value = SimpleColorFilter(MaterialTheme.colorScheme.primary.toArgb()),\n            keyPath = arrayOf(\"**\")\n        ),\n    )\n\n    LottieAnimation(\n        composition = trophyLottieComposition,\n        modifier = modifier.size(200.dp),\n        dynamicProperties = dynamicProperties\n    )\n}\n\n@Composable\n@PreviewLightDark\nprivate fun LevelCompletedContentPreview() {\n    NewQuizTheme {\n        Surface {\n            LevelCompletedContent(\n                onNext = {},\n                popBackStack = {},\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/main/kotlin/com/infinitepower/newquiz/feature/maze/level_results/components/LevelFailedContent.kt",
    "content": "package com.infinitepower.newquiz.feature.maze.level_results.components\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Close\nimport androidx.compose.material.icons.rounded.Home\nimport androidx.compose.material.icons.rounded.Refresh\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun LevelFailedContent(\n    onRetry: () -> Unit,\n    popBackStack: () -> Unit,\n    modifier: Modifier = Modifier,\n) {\n    Column(\n        modifier = modifier\n            .padding(MaterialTheme.spacing.large)\n            .fillMaxWidth(),\n        horizontalAlignment = Alignment.CenterHorizontally\n    ) {\n        Text(\n            text = stringResource(id = CoreR.string.level_failed),\n            style = MaterialTheme.typography.headlineLarge,\n            color = MaterialTheme.colorScheme.error\n        )\n        Icon(\n            imageVector = Icons.Rounded.Close,\n            contentDescription = null,\n            modifier = Modifier\n                .size(200.dp)\n                .padding(MaterialTheme.spacing.large)\n                .background(\n                    color = MaterialTheme.colorScheme.errorContainer,\n                    shape = CircleShape\n                ).padding(MaterialTheme.spacing.medium),\n            tint = MaterialTheme.colorScheme.error\n        )\n        Text(\n            text = stringResource(id = CoreR.string.level_failed_description),\n            style = MaterialTheme.typography.bodyLarge,\n            modifier = Modifier.padding(bottom = MaterialTheme.spacing.large)\n        )\n        Button(\n            onClick = onRetry,\n            modifier = Modifier.fillMaxWidth()\n        ) {\n            Icon(\n                imageVector = Icons.Rounded.Refresh,\n                contentDescription = null,\n                modifier = Modifier.size(ButtonDefaults.IconSize)\n            )\n            Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))\n            Text(text = stringResource(id = CoreR.string.try_again))\n        }\n        TextButton(\n            onClick = popBackStack,\n            modifier = Modifier.fillMaxWidth(),\n        ) {\n            Icon(\n                imageVector = Icons.Rounded.Home,\n                contentDescription = null,\n                modifier = Modifier.size(ButtonDefaults.IconSize)\n            )\n            Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))\n            Text(text = stringResource(id = CoreR.string.main_menu))\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun LevelFailedContentPreview() {\n    NewQuizTheme {\n        Surface {\n            LevelFailedContent(\n                onRetry = {},\n                popBackStack = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/maze/src/test/kotlin/com/infinitepower/newquiz/feature/maze/MazeScreenUiStateTest.kt",
    "content": "package com.infinitepower.newquiz.feature.maze\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.data.fake.FakeComparisonQuizData\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.maze.MazeQuiz\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionType\nimport com.infinitepower.newquiz.model.multi_choice_quiz.QuestionLanguage\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport kotlinx.collections.immutable.toImmutableList\nimport kotlin.test.Test\n\ninternal class MazeScreenUiStateTest {\n    @Test\n    fun `getAvailableCategoriesByGameMode returns empty when maze is empty`() {\n        val maze = MazeQuiz(items = generateMaze().toImmutableList())\n\n        val uiState = MazeScreenUiState(\n            loading = false,\n            maze = maze\n        )\n\n        val availableCategoriesByGameMode = uiState.getAvailableCategoriesByGameMode()\n        assertThat(availableCategoriesByGameMode).isEmpty()\n    }\n\n    @Test\n    fun `getAvailableCategoriesByGameMode returns all categories when all categories are available`() {\n        val maze = MazeQuiz(\n            items = generateMaze(\n                multiChoiceQuestionCount = 1,\n                wordleQuestionCount = 1,\n                comparisonQuizQuestionCount = 1\n            ).toImmutableList(),\n        )\n\n        val comparisonQuizCategory = FakeComparisonQuizData.generateCategory(1)\n\n        val uiState = MazeScreenUiState(\n            loading = false,\n            maze = maze,\n            comparisonQuizCategories = listOf(comparisonQuizCategory)\n        )\n\n        val availableCategoriesByGameMode = uiState.getAvailableCategoriesByGameMode()\n        availableCategoriesByGameMode.forEach { (gameMode, baseCategories) ->\n            println(\"Game Mode: $gameMode\")\n            assertThat(baseCategories).hasSize(1)\n        }\n    }\n\n    @Test\n    fun `getAvailableCategoriesByGameMode returns only available categories`() {\n        val maze = MazeQuiz(\n            items = generateMaze(\n                multiChoiceQuestionCount = 1,\n                wordleQuestionCount = 1,\n                comparisonQuizQuestionCount = 1\n            ).toImmutableList(),\n        )\n\n        val uiState = MazeScreenUiState(\n            loading = false,\n            maze = maze,\n            comparisonQuizCategories = emptyList() // No available comparison quiz categories\n        )\n\n        val availableCategoriesByGameMode = uiState.getAvailableCategoriesByGameMode()\n        availableCategoriesByGameMode.forEach { (gameMode, baseCategories) ->\n            if (gameMode == GameMode.COMPARISON_QUIZ) {\n                assertThat(baseCategories).hasSize(0)\n            } else {\n                assertThat(baseCategories).hasSize(1)\n            }\n        }\n    }\n\n    @Test\n    fun `getInvalidMazeItems returns correct items`() {\n        val maze = MazeQuiz(\n            items = generateMaze(\n                multiChoiceQuestionCount = 3,\n                wordleQuestionCount = 5,\n                comparisonQuizQuestionCount = 5 // 5 Invalid questions\n            ).toImmutableList(),\n        )\n\n        val uiState = MazeScreenUiState(\n            loading = false,\n            maze = maze,\n            comparisonQuizCategories = emptyList() // No available comparison quiz categories\n        )\n\n        val availableCategoriesByGameMode = uiState.getAvailableCategoriesByGameMode()\n        val invalidItems = uiState.getInvalidMazeItems(availableCategoriesByGameMode)\n        assertThat(invalidItems).hasSize(5)\n        invalidItems.forEach { item ->\n            assertThat(item.gameMode).isEqualTo(GameMode.COMPARISON_QUIZ)\n        }\n    }\n\n    private fun generateMaze(\n        seed: Int = 0,\n        multiChoiceQuestionCount: Int = 0,\n        wordleQuestionCount: Int = 0,\n        comparisonQuizQuestionCount: Int = 0\n    ): List<MazeQuiz.MazeItem> {\n        val items = mutableListOf<MazeQuiz.MazeItem>()\n\n        List(multiChoiceQuestionCount) {\n            MazeQuiz.MazeItem.MultiChoice(\n                question = MultiChoiceQuestion(\n                    id = it,\n                    description = \"description\",\n                    answers = listOf(\"a\", \"b\", \"c\"),\n                    lang = QuestionLanguage.EN,\n                    category = MultiChoiceBaseCategory.Flag,\n                    correctAns = 0,\n                    type = MultiChoiceQuestionType.MULTIPLE,\n                    difficulty = QuestionDifficulty.Easy\n                ),\n                mazeSeed = seed,\n            )\n        }.also { items.addAll(it) }\n\n        List(wordleQuestionCount) {\n            MazeQuiz.MazeItem.Wordle(\n                wordleWord = WordleWord(\"\"),\n                wordleQuizType = WordleQuizType.TEXT,\n                mazeSeed = seed,\n            )\n        }.also { items.addAll(it) }\n\n        List(comparisonQuizQuestionCount) {\n            MazeQuiz.MazeItem.ComparisonQuiz(\n                question = FakeComparisonQuizData.generateQuestion(categoryId = \"1\"),\n                mazeSeed = seed,\n            )\n        }.also { items.addAll(it) }\n\n        return items\n    }\n}\n"
  },
  {
    "path": "feature/profile/.gitignore",
    "content": "/build"
  },
  {
    "path": "feature/profile/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.feature)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.feature.profile\"\n}\n\ndependencies {\n    implementation(libs.androidx.lifecycle.livedata.ktx)\n\n    implementation(libs.vico.compose)\n    implementation(libs.vico.compose.m3)\n\n    implementation(libs.kotlinx.datetime)\n\n    implementation(libs.coil.kt.compose)\n\n    implementation(projects.core)\n    implementation(projects.core.userServices)\n}\n"
  },
  {
    "path": "feature/profile/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/ProfileScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.profile\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedCard\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.SegmentedButton\nimport androidx.compose.material3.SegmentedButtonDefaults\nimport androidx.compose.material3.SingleChoiceSegmentedButtonRow\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.core.user_services.DateTimeRangeFormatter\nimport com.infinitepower.newquiz.core.user_services.model.User\nimport com.infinitepower.newquiz.feature.profile.components.MainUserCard\nimport com.infinitepower.newquiz.feature.profile.components.UserXpAndLevelCard\nimport com.infinitepower.newquiz.feature.profile.components.XpEarnedByDayCard\nimport com.infinitepower.newquiz.model.TimestampWithXP\nimport com.infinitepower.newquiz.core.R as CoreR\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.collections.immutable.persistentListOf\nimport kotlinx.datetime.Clock\nimport kotlin.time.Duration.Companion.days\n\n@Composable\n@Destination\n@OptIn(ExperimentalMaterial3Api::class)\nfun ProfileScreen(\n    navigator: DestinationsNavigator,\n    viewModel: ProfileViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    ProfileScreen(\n        uiState = uiState,\n        onEvent = viewModel::onEvent,\n        onBackClick = navigator::popBackStack\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\nprivate fun ProfileScreen(\n    uiState: ProfileScreenUiState,\n    onEvent: (event: ProfileScreenUiEvent) -> Unit,\n    onBackClick: () -> Unit\n) {\n    val options = mapOf(\n        DateTimeRangeFormatter.Day to stringResource(id = CoreR.string.today),\n        DateTimeRangeFormatter.Week to stringResource(id = CoreR.string.this_week),\n    )\n\n    Scaffold(\n        topBar = {\n            TopAppBar(\n                title = { Text(text = stringResource(id = CoreR.string.profile)) },\n                navigationIcon = { BackIconButton(onClick = onBackClick) }\n            )\n        }\n    ) { innerPadding ->\n        LazyColumn(\n            modifier = Modifier.padding(innerPadding),\n            contentPadding = PaddingValues(MaterialTheme.spacing.medium),\n            verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.large)\n        ) {\n            if (uiState.loading) {\n                item { CircularProgressIndicator() }\n            }\n\n            uiState.user?.let { user ->\n                item {\n                    MainUserCard(\n                        modifier = Modifier.fillParentMaxWidth(),\n                        level = user.level,\n                        levelProgress = user.getLevelProgress(),\n                        userName = user.fullName,\n                        userPhoto = user.imageUrl\n                    )\n                }\n\n                item {\n                    UserXpAndLevelCard(\n                        modifier = Modifier.fillParentMaxWidth(),\n                        totalXp = user.totalXp,\n                        level = user.level\n                    )\n                }\n\n                item {\n                    SingleChoiceSegmentedButtonRow(\n                        modifier = Modifier.fillParentMaxWidth(),\n                    ) {\n                        options.onEachIndexed { index, (timeRange, label) ->\n                            SegmentedButton(\n                                shape = SegmentedButtonDefaults.itemShape(\n                                    index = index,\n                                    count = options.size\n                                ),\n                                onClick = {\n                                    onEvent(ProfileScreenUiEvent.OnFilterByTimeRangeClick(timeRange))\n                                },\n                                selected = timeRange == uiState.selectedTimeRange\n                            ) {\n                                Text(text = label)\n                            }\n                        }\n                    }\n                }\n\n                item {\n                    OutlinedCard(\n                        modifier = Modifier.fillParentMaxWidth()\n                    ) {\n                        XpEarnedByDayCard(\n                            modifier = Modifier.padding(MaterialTheme.spacing.medium),\n                            formatter = uiState.selectedTimeRange,\n                            xpEarnedList = uiState.xpEarnedList\n                        )\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun ProfileScreenPreview() {\n    val now = Clock.System.now()\n\n    NewQuizTheme {\n        ProfileScreen(\n            uiState = ProfileScreenUiState(\n                loading = false,\n                user = User(\n                    uid = \"uid\",\n                    totalXp = 1235u\n                ),\n                selectedTimeRange = DateTimeRangeFormatter.Week,\n                xpEarnedList = persistentListOf(\n                    TimestampWithXP((now - 4.days).toEpochMilliseconds(), 20),\n                    TimestampWithXP((now - 3.days).toEpochMilliseconds(), 10),\n                    TimestampWithXP((now - 1.days).toEpochMilliseconds(), 30),\n                    TimestampWithXP(now.toEpochMilliseconds(), 15)\n                )\n            ),\n            onEvent = {},\n            onBackClick = {}\n        )\n    }\n}\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/ProfileScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.feature.profile\n\nimport com.infinitepower.newquiz.core.user_services.DateTimeRangeFormatter\n\ninterface ProfileScreenUiEvent {\n    data class OnFilterByTimeRangeClick(val timeRange: DateTimeRangeFormatter) : ProfileScreenUiEvent\n}\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/ProfileScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.feature.profile\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.core.user_services.DateTimeRangeFormatter\nimport com.infinitepower.newquiz.core.user_services.model.User\nimport com.infinitepower.newquiz.model.TimestampWithXP\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\n\n@Keep\ndata class ProfileScreenUiState(\n    val loading: Boolean = true,\n    val user: User? = null,\n    val selectedTimeRange: DateTimeRangeFormatter = DateTimeRangeFormatter.Day,\n    val xpEarnedList: ImmutableList<TimestampWithXP> = persistentListOf(),\n)\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/ProfileViewModel.kt",
    "content": "package com.infinitepower.newquiz.feature.profile\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.collections.immutable.toPersistentList\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.map\nimport kotlinx.coroutines.flow.stateIn\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\nclass ProfileViewModel @Inject constructor(\n    private val userService: UserService\n) : ViewModel() {\n    private val _uiState = MutableStateFlow(ProfileScreenUiState())\n    val uiState = _uiState.map { state ->\n        val dateTimeRange = state.selectedTimeRange.getNowDateTimeRange()\n\n        state.copy(xpEarnedList = userService.getXpEarnedBy(dateTimeRange).toPersistentList())\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(),\n        initialValue = ProfileScreenUiState()\n    )\n\n    init {\n        viewModelScope.launch {\n            val user = userService.getUser()\n\n            _uiState.update { currentState ->\n                currentState.copy(\n                    loading = false,\n                    user = user\n                )\n            }\n        }\n    }\n\n    fun onEvent(event: ProfileScreenUiEvent) {\n        when (event) {\n            is ProfileScreenUiEvent.OnFilterByTimeRangeClick -> {\n                _uiState.update { currentState ->\n                    currentState.copy(selectedTimeRange = event.timeRange)\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/components/GoodDayText.kt",
    "content": "package com.infinitepower.newquiz.feature.profile.components\n\nimport androidx.annotation.StringRes\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.text.SpanStyle\nimport androidx.compose.ui.text.buildAnnotatedString\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.withStyle\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.R as CoreR\nimport kotlinx.datetime.Clock\nimport kotlinx.datetime.TimeZone\nimport kotlinx.datetime.toLocalDateTime\n\nprivate sealed class DayState(@StringRes val text: Int) {\n    data object Morning : DayState(text = CoreR.string.good_morning)\n\n    data object Afternoon : DayState(text = CoreR.string.good_afternoon)\n\n    data object Evening : DayState(text = CoreR.string.good_evening)\n\n    data object Night : DayState(text = CoreR.string.good_night)\n\n    companion object {\n        @Suppress(\"MagicNumber\")\n        fun fromHour(hour: Int): DayState {\n            return when (hour) {\n                in 6..11 -> Morning\n                in 12..17 -> Afternoon\n                in 18..23 -> Evening\n                else -> Night\n            }\n        }\n    }\n}\n\n@Composable\ninternal fun GoodDayText(\n    modifier: Modifier = Modifier,\n    name: String\n) {\n    val dayText = remember {\n        val tz = TimeZone.currentSystemDefault()\n        val now = Clock.System.now()\n        val localTime = now.toLocalDateTime(tz)\n\n        DayState.fromHour(localTime.hour)\n    }\n\n    GoodDayText(\n        modifier = modifier,\n        dayText = stringResource(id = dayText.text),\n        name = name\n    )\n}\n\n@Composable\nprivate fun GoodDayText(\n    modifier: Modifier = Modifier,\n    dayText: String,\n    name: String\n) {\n    Text(\n        text = buildAnnotatedString {\n            append(dayText)\n            append(\",\\n\")\n            withStyle(\n                style = SpanStyle(fontWeight = FontWeight.Bold)\n            ) {\n                append(name)\n            }\n        },\n        style = MaterialTheme.typography.headlineMedium,\n        modifier = modifier\n    )\n}\n\n@Composable\n@PreviewLightDark\nprivate fun GoodDayTextPreview() {\n    NewQuizTheme {\n        Surface {\n            GoodDayText(\n                name = \"NewQuiz User\",\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/components/MainUserCard.kt",
    "content": "package com.infinitepower.newquiz.feature.profile.components\n\nimport androidx.compose.animation.AnimatedContent\nimport androidx.compose.animation.core.animateFloatAsState\nimport androidx.compose.animation.core.updateTransition\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Person\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.ProgressIndicatorDefaults\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.StrokeCap\nimport androidx.compose.ui.graphics.vector.rememberVectorPainter\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport coil.compose.AsyncImage\nimport coil.request.ImageRequest\nimport com.infinitepower.newquiz.core.common.DEFAULT_USER_PHOTO\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun MainUserCard(\n    modifier: Modifier = Modifier,\n    level: UInt,\n    levelProgress: Float,\n    userName: String,\n    userPhoto: String,\n) {\n    Row(\n        modifier = modifier,\n        verticalAlignment = Alignment.CenterVertically,\n        horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.large, Alignment.CenterHorizontally)\n    ) {\n        LevelCard(\n            level = level,\n            levelProgress = levelProgress,\n            userPhoto = userPhoto,\n            userName = userName\n        )\n        GoodDayText(name = userName)\n    }\n}\n\nprivate enum class AvatarState {\n    LEVEL,\n    PHOTO;\n\n    fun toggle() = when (this) {\n        LEVEL -> PHOTO\n        PHOTO -> LEVEL\n    }\n}\n\n@Composable\nprivate fun LevelCard(\n    modifier: Modifier = Modifier,\n    level: UInt,\n    levelProgress: Float,\n    userPhoto: String,\n    userName: String,\n    initialAvatarState: AvatarState = AvatarState.LEVEL\n) {\n    val levelProgressAnimated = animateFloatAsState(\n        targetValue = levelProgress,\n        label = \"Level Progress\",\n        animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec\n    )\n\n    var avatarState by remember { mutableStateOf(initialAvatarState) }\n\n    val avatarLevelTransition = updateTransition(\n        targetState = avatarState,\n        label = \"Avatar Level\"\n    )\n\n    Surface(\n        modifier = modifier\n            .size(75.dp),\n        onClick = { avatarState = avatarState.toggle() },\n        shape = CircleShape\n    ) {\n        Box(\n            contentAlignment = Alignment.Center,\n            modifier = Modifier.fillMaxSize()\n        ) {\n            CircularProgressIndicator(\n                progress = { levelProgressAnimated.value },\n                modifier = Modifier.fillMaxSize(),\n                trackColor = ProgressIndicatorDefaults.linearTrackColor,\n                strokeCap = StrokeCap.Round,\n            )\n\n            avatarLevelTransition.AnimatedContent { state ->\n                when (state) {\n                    AvatarState.LEVEL -> {\n                        Box(\n                            contentAlignment = Alignment.Center,\n                            modifier = Modifier\n                                .fillMaxSize()\n                                .padding(MaterialTheme.spacing.small)\n                                .background(MaterialTheme.colorScheme.primary, CircleShape)\n                                .clip(CircleShape)\n                        ) {\n                            Text(\n                                text = level.toString(),\n                                style = MaterialTheme.typography.headlineLarge,\n                                color = MaterialTheme.colorScheme.onPrimary\n                            )\n                        }\n                    }\n                    AvatarState.PHOTO -> {\n                        AsyncImage(\n                            model = ImageRequest.Builder(LocalContext.current)\n                                .crossfade(true)\n                                .data(userPhoto)\n                                .build(),\n                            contentDescription = stringResource(id = CoreR.string.photo_of_s, userName),\n                            placeholder = rememberVectorPainter(Icons.Rounded.Person),\n                            modifier = Modifier\n                                .fillMaxSize()\n                                .padding(MaterialTheme.spacing.small)\n                                .clip(CircleShape),\n                            contentScale = ContentScale.Crop\n                        )\n                    }\n                }\n            }\n        }\n    }\n\n}\n\n@Composable\n@PreviewLightDark\nprivate fun MainUserCardPreview() {\n    NewQuizTheme {\n        Surface {\n            MainUserCard(\n                modifier = Modifier.padding(16.dp),\n                level = 3u,\n                levelProgress = 0.75f,\n                userName = \"NewQuiz User\",\n                userPhoto = DEFAULT_USER_PHOTO\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/components/UserXpAndLevelCard.kt",
    "content": "package com.infinitepower.newquiz.feature.profile.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxHeight\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.VerticalDivider\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.text.CompactDecimalText\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun UserXpAndLevelCard(\n    modifier: Modifier = Modifier,\n    totalXp: ULong,\n    level: UInt\n) {\n    Surface(\n        modifier = modifier.height(90.dp),\n        tonalElevation = 4.dp,\n        shape = MaterialTheme.shapes.extraLarge\n    ) {\n        Row(\n            modifier = Modifier.padding(MaterialTheme.spacing.medium)\n        ) {\n            CardItem(\n                modifier = Modifier.weight(1f),\n                title = stringResource(id = CoreR.string.level),\n                value = level.toInt()\n            )\n            VerticalDivider()\n            CardItem(\n                modifier = Modifier.weight(1f),\n                title = stringResource(id = CoreR.string.total_xp),\n                value = totalXp.toInt()\n            )\n        }\n    }\n}\n\n@Composable\nprivate fun CardItem(\n    modifier: Modifier = Modifier,\n    title: String,\n    value: Int\n) {\n    Column(\n        modifier = modifier.fillMaxHeight(),\n        horizontalAlignment = Alignment.CenterHorizontally,\n        verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.small, Alignment.CenterVertically)\n    ) {\n        Text(\n            text = title,\n            style = MaterialTheme.typography.bodyMedium\n        )\n        CompactDecimalText(\n            value = value,\n            style = MaterialTheme.typography.titleMedium\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun UserXpAndLevelCardPreview() {\n    NewQuizTheme {\n        Surface {\n            UserXpAndLevelCard(\n                modifier = Modifier.padding(16.dp),\n                totalXp = 1234u,\n                level = 1u\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/components/XpEarnedByDayCard.kt",
    "content": "package com.infinitepower.newquiz.feature.profile.components\n\nimport android.graphics.RectF\nimport android.graphics.Typeface\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.user_services.DateTimeRangeFormatter\nimport com.infinitepower.newquiz.feature.profile.components.chart.rememberMarker\nimport com.infinitepower.newquiz.model.TimestampWithXP\nimport com.patrykandpatryk.vico.compose.axis.axisGuidelineComponent\nimport com.patrykandpatryk.vico.compose.axis.axisLabelComponent\nimport com.infinitepower.newquiz.core.R as CoreR\nimport com.patrykandpatryk.vico.compose.axis.horizontal.rememberBottomAxis\nimport com.patrykandpatryk.vico.compose.axis.vertical.rememberStartAxis\nimport com.patrykandpatryk.vico.compose.chart.Chart\nimport com.patrykandpatryk.vico.compose.chart.column.columnChart\nimport com.patrykandpatryk.vico.compose.chart.edges.rememberFadingEdges\nimport com.patrykandpatryk.vico.compose.component.shapeComponent\nimport com.patrykandpatryk.vico.compose.component.textComponent as composeTextComponent\nimport com.patrykandpatryk.vico.compose.dimensions.dimensionsOf\nimport com.patrykandpatryk.vico.compose.m3.style.m3ChartStyle\nimport com.patrykandpatryk.vico.compose.style.ProvideChartStyle\nimport com.patrykandpatryk.vico.core.axis.AxisItemPlacer\nimport com.patrykandpatryk.vico.core.axis.AxisPosition\nimport com.patrykandpatryk.vico.core.axis.formatter.AxisValueFormatter\nimport com.patrykandpatryk.vico.core.axis.vertical.VerticalAxis\nimport com.patrykandpatryk.vico.core.chart.decoration.Decoration\nimport com.patrykandpatryk.vico.core.chart.draw.ChartDrawContext\nimport com.patrykandpatryk.vico.core.component.shape.Shapes\nimport com.patrykandpatryk.vico.core.component.text.TextComponent\nimport com.patrykandpatryk.vico.core.component.text.textComponent\nimport com.patrykandpatryk.vico.core.entry.entryModelOf\nimport com.patrykandpatryk.vico.core.entry.entryOf\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\nimport kotlinx.datetime.Clock\nimport kotlin.time.Duration.Companion.days\nimport kotlin.time.Duration.Companion.hours\n\n@Composable\ninternal fun XpEarnedByDayCard(\n    modifier: Modifier = Modifier,\n    formatter: DateTimeRangeFormatter,\n    xpEarnedList: ImmutableList<TimestampWithXP>\n) {\n    val resultsAggregated = remember(formatter, xpEarnedList) {\n        formatter.aggregateResults(xpEarnedList)\n    }\n\n    val xValuesToDates = remember(resultsAggregated) {\n        resultsAggregated.keys.associateBy { it.toFloat() }\n    }\n    val chartEntryModel = remember(xValuesToDates, resultsAggregated) {\n        entryModelOf(xValuesToDates.keys.zip(resultsAggregated.values, ::entryOf))\n    }\n    val horizontalAxisValueFormatter = remember(formatter, xValuesToDates) {\n        AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, _ ->\n            val data = xValuesToDates[value] ?: value.toInt()\n\n            formatter.formatValueToString(data)\n        }\n    }\n\n    val noDataTextComponent = composeTextComponent(\n        color = MaterialTheme.colorScheme.onSurface,\n        textSize = NO_DATA_TEXT_SIZE\n    )\n    val noDataText = stringResource(CoreR.string.no_data)\n\n    val decorations = remember(xValuesToDates) {\n        if (xValuesToDates.isEmpty()) {\n            listOf(\n                NoDataText(\n                    text = noDataText,\n                    textComponent = noDataTextComponent\n                ),\n            )\n        } else {\n            emptyList()\n        }\n    }\n\n    ProvideChartStyle(\n        chartStyle = m3ChartStyle(\n            axisGuidelineColor = MaterialTheme.colorScheme.outlineVariant,\n            axisLineColor = MaterialTheme.colorScheme.outlineVariant,\n        )\n    ) {\n        Chart(\n            modifier = modifier,\n            chart = columnChart(decorations = decorations),\n            model = chartEntryModel,\n            startAxis = rememberStartAxis(\n                label = if (resultsAggregated.isEmpty()) null else axisLabelComponent(),\n                valueFormatter = { value, _ -> value.toInt().toString() },\n                itemPlacer = startAxisItemPlacer,\n                horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Outside,\n                titleComponent = composeTextComponent(\n                    color = MaterialTheme.colorScheme.onPrimary,\n                    background = shapeComponent(\n                        shape = Shapes.pillShape,\n                        color = MaterialTheme.colorScheme.primary\n                    ),\n                    padding = axisTitlePadding,\n                    margins = startAxisTitleMargins,\n                    typeface = Typeface.MONOSPACE,\n                ),\n                title = stringResource(CoreR.string.xp),\n                guideline = if (resultsAggregated.isEmpty()) null else axisGuidelineComponent()\n            ),\n            bottomAxis = rememberBottomAxis(\n                label = if (resultsAggregated.isEmpty()) null else axisLabelComponent(),\n                valueFormatter = horizontalAxisValueFormatter,\n                guideline = null\n            ),\n            marker = rememberMarker(),\n            fadingEdges = rememberFadingEdges(),\n        )\n    }\n}\n\nprivate class NoDataText(\n    val text: String,\n    val textComponent: TextComponent = textComponent(),\n) : Decoration {\n    override fun onDrawAboveChart(context: ChartDrawContext, bounds: RectF) {\n        textComponent.drawText(\n            context = context,\n            text = text,\n            textX = bounds.centerX(),\n            textY = bounds.centerY(),\n        )\n    }\n}\n\nprivate const val MAX_START_AXIS_ITEM_COUNT = 6\nprivate val startAxisItemPlacer = AxisItemPlacer.Vertical.default(MAX_START_AXIS_ITEM_COUNT)\n\nprivate val axisTitleHorizontalPaddingValue = 8.dp\nprivate val axisTitleVerticalPaddingValue = 2.dp\nprivate val axisTitlePadding = dimensionsOf(\n    horizontal = axisTitleHorizontalPaddingValue,\n    vertical = axisTitleVerticalPaddingValue\n)\nprivate val axisTitleMarginValue = 4.dp\nprivate val startAxisTitleMargins = dimensionsOf(end = axisTitleMarginValue)\n\nprivate val NO_DATA_TEXT_SIZE = 18.sp\n\n@Composable\n@PreviewLightDark\nprivate fun EmptyXpCardPreview() {\n    NewQuizTheme {\n        Surface {\n            XpEarnedByDayCard(\n                modifier = Modifier.padding(16.dp),\n                formatter = DateTimeRangeFormatter.Day,\n                xpEarnedList = persistentListOf()\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun XpTodayCardPreview() {\n    val now = Clock.System.now()\n\n    NewQuizTheme {\n        Surface {\n            XpEarnedByDayCard(\n                modifier = Modifier.padding(16.dp),\n                formatter = DateTimeRangeFormatter.Day,\n                xpEarnedList = persistentListOf(\n                    TimestampWithXP((now - 4.hours).toEpochMilliseconds(), 10),\n                    TimestampWithXP((now - 3.hours).toEpochMilliseconds(), 5),\n                    TimestampWithXP(now.toEpochMilliseconds(), 15)\n                )\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun XpThisWeekCardPreview() {\n    val now = Clock.System.now()\n\n    NewQuizTheme {\n        Surface {\n            XpEarnedByDayCard(\n                modifier = Modifier.padding(16.dp),\n                formatter = DateTimeRangeFormatter.Week,\n                xpEarnedList = persistentListOf(\n                    TimestampWithXP((now - 4.days).toEpochMilliseconds(), 20),\n                    TimestampWithXP((now - 3.days).toEpochMilliseconds(), 10),\n                    TimestampWithXP((now - 1.days).toEpochMilliseconds(), 30),\n                    TimestampWithXP(now.toEpochMilliseconds(), 15)\n                )\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/profile/src/main/kotlin/com/infinitepower/newquiz/feature/profile/components/chart/Marker.kt",
    "content": "package com.infinitepower.newquiz.feature.profile.components.chart\n\nimport android.graphics.Typeface\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.toArgb\nimport androidx.compose.ui.unit.dp\nimport com.patrykandpatryk.vico.compose.component.lineComponent\nimport com.patrykandpatryk.vico.compose.component.overlayingComponent\nimport com.patrykandpatryk.vico.compose.component.shapeComponent\nimport com.patrykandpatryk.vico.compose.component.textComponent\nimport com.patrykandpatryk.vico.compose.dimensions.dimensionsOf\nimport com.patrykandpatryk.vico.core.chart.dimensions.HorizontalDimensions\nimport com.patrykandpatryk.vico.core.chart.insets.Insets\nimport com.patrykandpatryk.vico.core.component.marker.MarkerComponent\nimport com.patrykandpatryk.vico.core.component.shape.DashedShape\nimport com.patrykandpatryk.vico.core.component.shape.ShapeComponent\nimport com.patrykandpatryk.vico.core.component.shape.Shapes\nimport com.patrykandpatryk.vico.core.component.shape.cornered.Corner\nimport com.patrykandpatryk.vico.core.component.shape.cornered.MarkerCorneredShape\nimport com.patrykandpatryk.vico.core.context.MeasureContext\nimport com.patrykandpatryk.vico.core.extension.copyColor\nimport com.patrykandpatryk.vico.core.marker.Marker\n\n@Composable\ninternal fun rememberMarker(): Marker {\n    val labelBackgroundColor = MaterialTheme.colorScheme.surface\n    val labelBackground = remember(labelBackgroundColor) {\n        ShapeComponent(labelBackgroundShape, labelBackgroundColor.toArgb()).setShadow(\n            radius = LABEL_BACKGROUND_SHADOW_RADIUS,\n            dy = LABEL_BACKGROUND_SHADOW_DY,\n            applyElevationOverlay = true,\n        )\n    }\n    val label = textComponent(\n        background = labelBackground,\n        lineCount = LABEL_LINE_COUNT,\n        padding = labelPadding,\n        typeface = Typeface.MONOSPACE,\n    )\n    val indicatorInnerComponent = shapeComponent(Shapes.pillShape, MaterialTheme.colorScheme.surface)\n    val indicatorCenterComponent = shapeComponent(Shapes.pillShape, Color.White)\n    val indicatorOuterComponent = shapeComponent(Shapes.pillShape, Color.White)\n    val indicator = overlayingComponent(\n        outer = indicatorOuterComponent,\n        inner = overlayingComponent(\n            outer = indicatorCenterComponent,\n            inner = indicatorInnerComponent,\n            innerPaddingAll = indicatorInnerAndCenterComponentPaddingValue,\n        ),\n        innerPaddingAll = indicatorCenterAndOuterComponentPaddingValue,\n    )\n    val guideline = lineComponent(\n        MaterialTheme.colorScheme.onSurface.copy(GUIDELINE_ALPHA),\n        guidelineThickness,\n        guidelineShape,\n    )\n    return remember(label, indicator, guideline) {\n        object : MarkerComponent(label, indicator, guideline) {\n            init {\n                indicatorSizeDp = INDICATOR_SIZE_DP\n                onApplyEntryColor = { entryColor ->\n                    indicatorOuterComponent.color = entryColor.copyColor(\n                        INDICATOR_OUTER_COMPONENT_ALPHA\n                    )\n                    with(indicatorCenterComponent) {\n                        color = entryColor\n                        setShadow(radius = INDICATOR_CENTER_COMPONENT_SHADOW_RADIUS, color = entryColor)\n                    }\n                }\n            }\n\n            override fun getInsets(\n                context: MeasureContext,\n                outInsets: Insets,\n                horizontalDimensions: HorizontalDimensions,\n            ) = with(context) {\n                outInsets.top = label.getHeight(context) + labelBackgroundShape.tickSizeDp.pixels +\n                        LABEL_BACKGROUND_SHADOW_RADIUS.pixels * SHADOW_RADIUS_MULTIPLIER -\n                        LABEL_BACKGROUND_SHADOW_DY.pixels\n            }\n        }\n    }\n}\n\nprivate const val LABEL_BACKGROUND_SHADOW_RADIUS = 4f\nprivate const val LABEL_BACKGROUND_SHADOW_DY = 2f\nprivate const val LABEL_LINE_COUNT = 1\nprivate const val GUIDELINE_ALPHA = .2f\nprivate const val INDICATOR_SIZE_DP = 36f\nprivate const val INDICATOR_OUTER_COMPONENT_ALPHA = 32\nprivate const val INDICATOR_CENTER_COMPONENT_SHADOW_RADIUS = 12f\nprivate const val GUIDELINE_DASH_LENGTH_DP = 8f\nprivate const val GUIDELINE_GAP_LENGTH_DP = 4f\nprivate const val SHADOW_RADIUS_MULTIPLIER = 1.3f\n\nprivate val labelBackgroundShape = MarkerCorneredShape(Corner.FullyRounded)\nprivate val labelHorizontalPaddingValue = 8.dp\nprivate val labelVerticalPaddingValue = 4.dp\nprivate val labelPadding = dimensionsOf(labelHorizontalPaddingValue, labelVerticalPaddingValue)\nprivate val indicatorInnerAndCenterComponentPaddingValue = 5.dp\nprivate val indicatorCenterAndOuterComponentPaddingValue = 10.dp\nprivate val guidelineThickness = 2.dp\nprivate val guidelineShape = DashedShape(Shapes.pillShape, GUIDELINE_DASH_LENGTH_DP, GUIDELINE_GAP_LENGTH_DP)\n"
  },
  {
    "path": "feature/settings/.gitignore",
    "content": "/build"
  },
  {
    "path": "feature/settings/README.md",
    "content": "# :feature:settings module\n"
  },
  {
    "path": "feature/settings/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.feature)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n    alias(libs.plugins.newquiz.detekt)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.feature.settings\"\n}\n\ndependencies {\n    implementation(libs.androidx.compose.material3.windowSizeClass)\n    implementation(libs.androidx.compose.material.iconsExtended)\n\n    implementation(libs.google.oss.licenses) {\n        exclude(group = \"androidx.appcompat\")\n    }\n\n    implementation(projects.core.datastore)\n    implementation(projects.domain)\n    implementation(projects.data)\n    normalImplementation(projects.core.translation)\n}\n"
  },
  {
    "path": "feature/settings/src/foss/kotlin/com/infinitepower/newquiz/feature/settings/common/BuildVariant.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.common\n\nobject BuildVariant {\n    internal const val DISTRIBUTION_FLAVOR = \"foss\"\n}\n"
  },
  {
    "path": "feature/settings/src/foss/kotlin/com/infinitepower/newquiz/feature/settings/screens/PreferencesScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens\n\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport com.infinitepower.newquiz.feature.settings.model.ScreenKey\nimport com.infinitepower.newquiz.feature.settings.screens.about_and_help.AboutAndHelpScreen\nimport com.infinitepower.newquiz.feature.settings.screens.animations.AnimationsScreen\nimport com.infinitepower.newquiz.feature.settings.screens.general.GeneralScreen\nimport com.infinitepower.newquiz.feature.settings.screens.multi_choice_quiz.MultiChoiceQuizScreen\nimport com.infinitepower.newquiz.feature.settings.screens.wordle.WordleScreen\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun PreferencesScreen(\n    modifier: Modifier = Modifier,\n    currentScreen: ScreenKey?,\n    isScreenExpanded: Boolean,\n    onBackClick: () -> Unit,\n    navigateToScreen: (key: ScreenKey) -> Unit,\n) {\n    when (currentScreen) {\n        null -> {}\n        ScreenKey.GENERAL -> GeneralScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n            navigateToScreen = navigateToScreen\n        )\n        ScreenKey.MULTI_CHOICE_QUIZ -> MultiChoiceQuizScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        ScreenKey.WORDLE -> WordleScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        ScreenKey.ABOUT_AND_HELP -> AboutAndHelpScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        ScreenKey.ANIMATIONS -> AnimationsScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        // Don't exist in the foss version\n        ScreenKey.ANALYTICS -> {}\n        ScreenKey.TRANSLATION -> {}\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/SettingsScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.foundation.layout.BoxWithConstraints\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.WindowSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowWidthSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.RectangleShape\nimport androidx.compose.ui.tooling.preview.PreviewScreenSizes\nimport androidx.compose.ui.unit.DpSize\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.feature.settings.destinations.SettingsScreenDestination\nimport com.infinitepower.newquiz.feature.settings.model.ScreenKey\nimport com.infinitepower.newquiz.feature.settings.screens.PreferencesScreen\nimport com.infinitepower.newquiz.feature.settings.screens.main.MainScreen\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator\n\n@Composable\n@Destination\n@OptIn(ExperimentalMaterial3Api::class)\nfun SettingsScreen(\n    screenKey: ScreenKey? = null,\n    windowSizeClass: WindowSizeClass,\n    navigator: DestinationsNavigator,\n) {\n    val isScreenExpanded = remember(windowSizeClass.widthSizeClass) {\n        windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded\n    }\n\n    SettingsContainer(\n        currentScreen = screenKey,\n        windowWidthSizeClass = windowSizeClass.widthSizeClass,\n        mainContent = {\n            MainScreen(\n                currentScreen = screenKey,\n                onScreenSelect = { key ->\n                    navigator.navigate(SettingsScreenDestination(key))\n                },\n                onBackClick = navigator::popBackStack,\n            )\n        },\n        preferencesContent = {\n            PreferencesScreen(\n                currentScreen = screenKey,\n                isScreenExpanded = isScreenExpanded,\n                onBackClick = navigator::popBackStack,\n                navigateToScreen = { key ->\n                    navigator.navigate(SettingsScreenDestination(key))\n                }\n            )\n        }\n    )\n}\n\n@Composable\nprivate fun SettingsContainer(\n    modifier: Modifier = Modifier,\n    currentScreen: ScreenKey?,\n    windowWidthSizeClass: WindowWidthSizeClass,\n    mainContent: @Composable BoxScope.() -> Unit,\n    preferencesContent: @Composable BoxScope.() -> Unit\n) {\n    val isInMainScreen = remember(currentScreen) { currentScreen == null }\n    val isExpanded = remember(windowWidthSizeClass) {\n        windowWidthSizeClass == WindowWidthSizeClass.Expanded\n    }\n\n    // If the screen is not in the main screen and the window is expanded, we want to show the\n    // main content in the left side of the screen and the preferences content in the right side.\n    // Otherwise, we only show the current screen content.\n    val inPreferencesAndExpanded = remember(isInMainScreen, isExpanded) {\n        !isInMainScreen && isExpanded\n    }\n\n    val leftContent = if (isInMainScreen || isExpanded) mainContent else preferencesContent\n\n    Row(modifier = modifier.fillMaxSize()) {\n        Surface(\n            modifier = Modifier\n                .weight(weight = 1f)\n                .padding(if (inPreferencesAndExpanded) MaterialTheme.spacing.medium else 0.dp),\n            tonalElevation = if (inPreferencesAndExpanded) 4.dp else 0.dp,\n            shape = if (inPreferencesAndExpanded) MaterialTheme.shapes.extraLarge else RectangleShape,\n        ) {\n            Box(\n                modifier = Modifier.padding(\n                    if (inPreferencesAndExpanded) MaterialTheme.spacing.medium else 0.dp\n                ),\n                content = leftContent\n            )\n        }\n\n        if (inPreferencesAndExpanded) {\n            Box(\n                modifier = Modifier.weight(2f),\n                content = preferencesContent\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewScreenSizes\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)\nprivate fun SettingsScreenPreview() {\n    BoxWithConstraints {\n        val windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight))\n\n        NewQuizTheme {\n            Surface {\n                SettingsScreen(\n                    screenKey = ScreenKey.WORDLE,\n                    windowSizeClass = windowSizeClass,\n                    navigator = EmptyDestinationsNavigator\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/AboutAndHelpButtons.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components\n\nimport android.content.Intent\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.automirrored.rounded.ContactSupport\nimport androidx.compose.material.icons.rounded.Balance\nimport androidx.compose.material.icons.rounded.Favorite\nimport androidx.compose.material.icons.rounded.Update\nimport androidx.compose.material3.FilledIconButton\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButtonDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.platform.LocalUriHandler\nimport androidx.compose.ui.res.vectorResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport com.google.android.gms.oss.licenses.OssLicensesMenuActivity\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.R as CoreR\n\nprivate const val NEWQUIZ_REPOSITORY_LINK = \"https://github.com/joaomanaia/newquiz\"\n\n@Composable\ninternal fun AboutAndHelpButtons(\n    modifier: Modifier = Modifier,\n    iconSize: Dp = 50.dp\n) {\n    val uriHandler = LocalUriHandler.current\n    val context = LocalContext.current\n\n    Row(\n        modifier = modifier,\n        verticalAlignment = Alignment.CenterVertically,\n        horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.medium)\n    ) {\n        FilledIconButton(\n            imageVector = ImageVector.vectorResource(id = CoreR.drawable.github_logo),\n            contentDescription = \"Github repository\",\n            onClick = { uriHandler.openUri(NEWQUIZ_REPOSITORY_LINK) },\n            modifier = Modifier.size(iconSize)\n        )\n        FilledIconButton(\n            imageVector = Icons.AutoMirrored.Rounded.ContactSupport,\n            contentDescription = \"Contact support\",\n            onClick = { uriHandler.openUri(\"$NEWQUIZ_REPOSITORY_LINK/issues\") },\n            modifier = Modifier.size(iconSize)\n        )\n        FilledIconButton(\n            imageVector = Icons.Rounded.Update,\n            contentDescription = \"Update\",\n            onClick = { uriHandler.openUri(\"$NEWQUIZ_REPOSITORY_LINK/releases\") },\n            modifier = Modifier.size(iconSize)\n        )\n        FilledIconButton(\n            imageVector = Icons.Rounded.Balance,\n            contentDescription = \"Open source licences\",\n            onClick = {\n                context.startActivity(\n                    Intent(context, OssLicensesMenuActivity::class.java).apply {\n                        action = Intent.ACTION_VIEW\n                    }\n                )\n            },\n            modifier = Modifier.size(iconSize)\n        )\n    }\n}\n\n@Composable\ninternal fun FilledIconButton(\n    modifier: Modifier = Modifier,\n    onClick: () -> Unit,\n    imageVector: ImageVector,\n    contentDescription: String?,\n    containerColor: Color = MaterialTheme.colorScheme.primary\n) {\n    FilledIconButton(\n        modifier = modifier,\n        onClick = onClick,\n        colors = IconButtonDefaults.filledIconButtonColors(containerColor = containerColor)\n    ) {\n        Icon(\n            imageVector = imageVector,\n            contentDescription = contentDescription\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun AboutAndHelpButtonsPreview() {\n    NewQuizTheme {\n        Surface {\n            AboutAndHelpButtons(\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun PrimaryButtonPreview() {\n    NewQuizTheme {\n        Surface {\n            FilledIconButton(\n                modifier = Modifier.padding(16.dp),\n                imageVector = Icons.Rounded.Favorite,\n                contentDescription = \"Favorite\",\n                onClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/PreferenceGroupHeader.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.LazyItemScope\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\n\n@Composable\ninternal fun LazyItemScope.PreferenceGroupHeader(title: String) {\n    val spaceSmall = MaterialTheme.spacing.small\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    Box(\n        contentAlignment = Alignment.CenterStart,\n        modifier = Modifier\n            .fillParentMaxWidth()\n            .padding(bottom = spaceSmall, top = spaceMedium)\n    ) {\n        Text(\n            text = title,\n            style = MaterialTheme.typography.labelLarge,\n            modifier = Modifier.padding(start = spaceMedium),\n            color = MaterialTheme.colorScheme.primary\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun PreferenceGroupHeaderPreview() {\n    NewQuizTheme {\n        Surface {\n            LazyColumn {\n                item {\n                    PreferenceGroupHeader(\"NewSocial\")\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/PreferenceItem.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.datastore.preferences.core.Preferences\nimport com.infinitepower.newquiz.core.compose.preferences.LocalPreferenceEnabledStatus\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.feature.settings.components.preferences.widgets.DropDownPreferenceWidget\nimport com.infinitepower.newquiz.feature.settings.components.preferences.widgets.ListPreferenceWidget\nimport com.infinitepower.newquiz.feature.settings.components.preferences.widgets.MultiSelectListPreferenceWidget\nimport com.infinitepower.newquiz.feature.settings.components.preferences.widgets.NavigationButtonWidget\nimport com.infinitepower.newquiz.feature.settings.components.preferences.widgets.SeekBarPreferenceWidget\nimport com.infinitepower.newquiz.feature.settings.components.preferences.widgets.SwitchPreferenceWidget\nimport com.infinitepower.newquiz.feature.settings.components.preferences.widgets.TextPreferenceWidget\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport kotlinx.collections.immutable.toImmutableSet\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\n@Composable\ninternal fun PreferenceItem(\n    item: Preference.PreferenceItem<*>,\n    prefs: Preferences?,\n    dataStoreManager: DataStoreManager\n) {\n    val scope = rememberCoroutineScope()\n\n    when (item) {\n        is Preference.PreferenceItem.NavigationButton -> {\n            NavigationButtonWidget(preference = item)\n        }\n\n        is Preference.PreferenceItem.SwitchPreference -> {\n            val enabled = LocalPreferenceEnabledStatus.current && item.enabled\n\n            SwitchPreferenceWidget(\n                preference = item,\n                checked = prefs?.get(item.request.key) ?: item.request.defaultValue,\n                onCheckChange = { newValue ->\n                    scope.launch(Dispatchers.IO) {\n                        item.onCheckChange(newValue && enabled)\n                        dataStoreManager.editPreference(item.request.key, newValue)\n                    }\n                }\n            )\n        }\n\n        is Preference.PreferenceItem.ListPreference -> {\n            ListPreferenceWidget(\n                preference = item,\n                value = prefs?.get(item.request.key) ?: item.request.defaultValue,\n                onValueChange = { newValue ->\n                    scope.launch(Dispatchers.IO) {\n                        dataStoreManager.editPreference(\n                            item.request.key,\n                            newValue\n                        )\n                    }\n                }\n            )\n        }\n\n        is Preference.PreferenceItem.SeekBarPreference -> {\n            SeekBarPreferenceWidget(\n                preference = item,\n                value = prefs?.get(item.request.key) ?: item.request.defaultValue,\n                onValueChange = { newValue ->\n                    scope.launch(Dispatchers.IO) {\n                        dataStoreManager.editPreference(\n                            item.request.key,\n                            newValue\n                        )\n                    }\n                }\n            )\n        }\n\n        is Preference.PreferenceItem.DropDownMenuPreference -> {\n            DropDownPreferenceWidget(\n                preference = item,\n                value = prefs?.get(item.request.key) ?: item.request.defaultValue,\n                onValueChange = { newValue ->\n                    scope.launch(Dispatchers.IO) {\n                        dataStoreManager.editPreference(\n                            item.request.key,\n                            newValue\n                        )\n                    }\n                }\n            )\n        }\n\n        is Preference.PreferenceItem.TextPreference -> {\n            TextPreferenceWidget(\n                preference = item,\n                onClick = item.onClick\n            )\n        }\n\n        is Preference.PreferenceItem.MultiSelectListPreference -> {\n            val values = remember {\n                (prefs?.get(item.request.key) ?: item.request.defaultValue).toImmutableSet()\n            }\n\n            MultiSelectListPreferenceWidget(\n                preference = item,\n                values = values,\n                onValuesChange = { newValues ->\n                    scope.launch(Dispatchers.IO) {\n                        dataStoreManager.editPreference(\n                            item.request.key,\n                            newValues\n                        )\n                    }\n                }\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/widgets/CustomPreferenceWidget.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences.widgets\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.feature.settings.model.Preference\n\n@Composable\ninternal fun CustomPreferenceWidget(\n    preference: Preference.CustomPreference\n) {\n    Box(\n        modifier = Modifier.semantics {\n            contentDescription = preference.title\n        },\n        content = preference.content\n    )\n}\n\n@Composable\n@PreviewLightDark\nprivate fun CustomPreferenceWidgetPreview() {\n    NewQuizTheme {\n        Surface {\n            CustomPreferenceWidget(\n                preference = Preference.CustomPreference(\n                    title = \"Custom Preference\",\n                    content = {\n                        Text(text = \"Custom Preference\")\n                    }\n                )\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/widgets/DropDownPreferenceWidget.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences.widgets\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.DropdownMenu\nimport androidx.compose.material3.DropdownMenuItem\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.feature.settings.model.Preference\n\n@Composable\ninternal fun DropDownPreferenceWidget(\n    preference: Preference.PreferenceItem.DropDownMenuPreference,\n    value: String,\n    onValueChange: (String) -> Unit\n) {\n    val (isExpanded, expand) = remember { mutableStateOf(false) }\n\n    TextPreferenceWidgetRes(\n        preference = preference,\n        summary = preference.entries[value],\n        onClick = { expand(!isExpanded) }\n    )\n\n    Box(\n        modifier = Modifier.padding(start = MaterialTheme.spacing.extraLarge)\n    ) {\n        DropdownMenu(\n            expanded = isExpanded,\n            onDismissRequest = { expand(!isExpanded) }\n        ) {\n            preference.entries.forEach { item ->\n                DropdownMenuItem(\n                    onClick = {\n                        onValueChange(item.key)\n                        expand(!isExpanded)\n                    },\n                    text = {\n                        Text(\n                            text = item.value,\n                            style = MaterialTheme.typography.bodySmall.merge()\n                        )\n                    }\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/widgets/ListPreferenceWidget.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences.widgets\n\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.foundation.selection.selectable\nimport androidx.compose.foundation.selection.selectableGroup\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.ListItem\nimport androidx.compose.material3.ListItemDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.RadioButton\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun ListPreferenceWidget(\n    preference: Preference.PreferenceItem.ListPreference,\n    value: String,\n    onValueChange: (String) -> Unit\n) {\n    val (isDialogShown, showDialog) = remember { mutableStateOf(false) }\n    val dismissDialog = { showDialog(false) }\n\n    val (newValue, setNewValue) = remember(value) {\n        mutableStateOf(value)\n    }\n\n    TextPreferenceWidget(\n        preference = preference,\n        summary = preference.entries[value],\n        onClick = { showDialog(!isDialogShown) },\n    )\n\n    if (isDialogShown) {\n        AlertDialog(\n            onDismissRequest = dismissDialog,\n            title = { Text(text = preference.title) },\n            text = {\n                LazyColumn(modifier = Modifier.selectableGroup()) {\n                    items(preference.entries.keys.toList()) { key ->\n                        val isSelected = newValue == key\n                        val onSelected = { setNewValue(key) }\n\n                        SelectableListItem(\n                            text = preference.entries[key].orEmpty(),\n                            isSelected = isSelected,\n                            onClick = onSelected\n                        )\n                    }\n                }\n            },\n            confirmButton = {\n                TextButton(\n                    onClick = {\n                        dismissDialog()\n                        preference.onItemClick(newValue)\n                        onValueChange(newValue)\n                    }\n                ) {\n                    Text(text = stringResource(id = CoreR.string.confirm))\n                }\n            },\n            dismissButton = {\n                TextButton(onClick = dismissDialog) {\n                    Text(text = stringResource(id = CoreR.string.dismiss))\n                }\n            }\n        )\n    }\n}\n\n@Composable\nprivate fun SelectableListItem(\n    modifier: Modifier = Modifier,\n    onClick: () -> Unit,\n    text: String,\n    isSelected: Boolean\n) {\n    ListItem(\n        headlineContent = { Text(text = text) },\n        leadingContent = {\n            RadioButton(\n                selected = isSelected,\n                onClick = onClick,\n            )\n        },\n        modifier = modifier\n            .clip(MaterialTheme.shapes.medium)\n            .selectable(\n                selected = isSelected,\n                onClick = onClick,\n                role = Role.RadioButton\n            ),\n        colors = ListItemDefaults.colors(\n            containerColor = Color.Transparent\n        )\n    )\n}\n\n@Composable\n@PreviewLightDark\nprivate fun SelectableListItemPreview() {\n    NewQuizTheme {\n        Surface {\n            SelectableListItem(\n                text = \"NewQuiz\",\n                isSelected = true,\n                onClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/widgets/MultiSelectListPreferenceWidget.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences.widgets\n\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.rememberScrollState\nimport androidx.compose.foundation.selection.selectable\nimport androidx.compose.foundation.verticalScroll\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.Checkbox\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport kotlinx.collections.immutable.ImmutableSet\n\n@Composable\ninternal fun MultiSelectListPreferenceWidget(\n    preference: Preference.PreferenceItem.MultiSelectListPreference,\n    values: ImmutableSet<String>,\n    onValuesChange: (Set<String>) -> Unit\n) {\n    val (isDialogShown, showDialog) = remember { mutableStateOf(false) }\n\n    val description = remember(preference.entries, values) {\n        preference\n            .entries\n            .filter { values.contains(it.key) }\n            .map { it.value }\n            .joinToString(separator = \", \", limit = 3)\n    }\n\n    TextPreferenceWidget(\n        preference = preference,\n        summary = description.ifBlank { null },\n        onClick = { showDialog(!isDialogShown) }\n    )\n\n    if (isDialogShown) {\n        AlertDialog(\n            onDismissRequest = { showDialog(false) },\n            title = { Text(text = preference.title) },\n            text = {\n                Column(\n                    modifier = Modifier\n                        .padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 16.dp)\n                        .verticalScroll(state = rememberScrollState())\n                ) {\n                    preference.entries.forEach { current ->\n                        val isSelected = values.contains(current.key)\n                        val onSelectionChanged = {\n                            val result = when (!isSelected) {\n                                true -> values + current.key\n                                false -> values - current.key\n                            }\n                            onValuesChange(result)\n                        }\n                        Row(\n                            verticalAlignment = Alignment.CenterVertically,\n                            modifier = Modifier\n                                .fillMaxWidth()\n                                .selectable(\n                                    selected = isSelected,\n                                    onClick = { onSelectionChanged() }\n                                )\n                                .padding(4.dp)\n                        ) {\n                            Checkbox(\n                                checked = isSelected,\n                                onCheckedChange = { onSelectionChanged() }\n                            )\n                            Text(\n                                text = current.value,\n                                style = MaterialTheme.typography.bodyLarge.merge(),\n                                modifier = Modifier.padding(start = 16.dp)\n                            )\n                        }\n                    }\n                }\n            },\n            confirmButton = {\n                TextButton(\n                    onClick = { showDialog(false) },\n                    colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.secondary),\n                ) {\n                    Text(text = stringResource(android.R.string.ok))\n                }\n            }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/widgets/NavigationButtonWidget.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences.widgets\n\nimport androidx.compose.animation.animateColor\nimport androidx.compose.animation.core.updateTransition\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Build\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.ListItem\nimport androidx.compose.material3.ListItemDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.feature.settings.model.Preference\n\n@Composable\ninternal fun NavigationButtonWidget(\n    modifier: Modifier = Modifier,\n    preference: Preference.PreferenceItem.NavigationButton\n) {\n    NavigationButton(\n        modifier = modifier,\n        title = preference.title,\n        icon = preference.icon,\n        description = preference.summary,\n        isSelected = preference.itemSelected,\n        enabled = preference.enabled,\n        singleLineTitle = preference.singleLineTitle,\n        singleLineSummary = preference.singleLineSummary,\n        onClick = preference.onClick,\n    )\n}\n\n@Composable\ninternal fun NavigationButton(\n    modifier: Modifier = Modifier,\n    onClick: () -> Unit,\n    title: String,\n    icon: @Composable (() -> Unit)?,\n    description: String? = null,\n    isSelected: Boolean = false,\n    enabled: Boolean = true,\n    singleLineTitle: Boolean = true,\n    singleLineSummary: Boolean = true,\n) {\n    val transition = updateTransition(\n        targetState = isSelected,\n        label = \"Navigation Button\"\n    )\n\n    val containerColor = transition.animateColor(\n        label = \"Container Color\"\n    ) { selected ->\n        if (selected) {\n            MaterialTheme.colorScheme.primaryContainer\n        } else {\n            MaterialTheme.colorScheme.surface\n        }\n    }\n\n    Surface(\n        modifier = modifier,\n        selected = isSelected,\n        onClick = onClick,\n        shape = MaterialTheme.shapes.extraLarge,\n        color = containerColor.value,\n        enabled = enabled,\n    ) {\n        ListItem(\n            headlineContent = {\n                Text(\n                    text = title,\n                    maxLines = if (singleLineTitle) 1 else Int.MAX_VALUE\n                )\n            },\n            supportingContent = description?.let {\n                {\n                    Text(\n                        text = it,\n                        maxLines = if (singleLineSummary) 1 else Int.MAX_VALUE,\n                        overflow = TextOverflow.Ellipsis\n                    )\n                }\n            },\n            leadingContent = icon,\n            colors = ListItemDefaults.colors(\n                containerColor = containerColor.value\n            ),\n            modifier = Modifier.padding(MaterialTheme.spacing.small),\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun NavigationButtonPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) isSelected: Boolean,\n) {\n    NewQuizTheme {\n        Surface {\n            NavigationButton(\n                title = \"Settings\",\n                icon = {\n                    Icon(\n                        imageVector = Icons.Rounded.Build,\n                        contentDescription = null\n                    )\n                },\n                description = \"Settings description\",\n                isSelected = isSelected,\n                onClick = {},\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/widgets/SeekBarPreferenceWidget.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences.widgets\n\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Slider\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableIntStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport com.infinitepower.newquiz.core.compose.preferences.LocalPreferenceEnabledStatus\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport kotlin.math.roundToInt\n\n@Composable\ninternal fun SeekBarPreferenceWidget(\n    preference: Preference.PreferenceItem.SeekBarPreference,\n    value: Int,\n    onValueChange: (Int) -> Unit,\n) {\n    var currentValue by remember(value) { mutableIntStateOf(value) }\n\n    TextPreferenceWidget(\n        preference = preference,\n        summary = {\n            PreferenceSummary(\n                preference = preference,\n                sliderValue = currentValue,\n                onValueChange = { currentValue = it },\n                onValueChangeEnd = { onValueChange(currentValue) }\n            )\n        }\n    )\n}\n\n@Composable\nprivate fun PreferenceSummary(\n    preference: Preference.PreferenceItem.SeekBarPreference,\n    sliderValue: Int,\n    onValueChange: (Int) -> Unit,\n    onValueChangeEnd: () -> Unit,\n) {\n    val isEnabled = LocalPreferenceEnabledStatus.current && preference.enabled\n\n    Column {\n        preference.summary?.let { Text(text = it) }\n\n        Row(verticalAlignment = Alignment.CenterVertically) {\n            Text(text = preference.valueRepresentation(sliderValue))\n            Spacer(modifier = Modifier.width(MaterialTheme.spacing.medium))\n            Slider(\n                value = sliderValue.toFloat(),\n                onValueChange = { if (preference.enabled) onValueChange(it.roundToInt()) },\n                valueRange = preference.valueRange.toClosedFloatingPointRange(),\n                steps = preference.steps,\n                onValueChangeFinished = onValueChangeEnd,\n                enabled = isEnabled\n            )\n        }\n    }\n}\n\nprivate fun ClosedRange<Int>.toClosedFloatingPointRange(): ClosedFloatingPointRange<Float> {\n    return start.toFloat()..endInclusive.toFloat()\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/widgets/SwitchPreferenceWidget.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences.widgets\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Switch\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.dp\nimport androidx.datastore.preferences.core.booleanPreferencesKey\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.compose.preferences.LocalPreferenceEnabledStatus\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.feature.settings.model.Preference\n\n@Composable\ninternal fun SwitchPreferenceWidget(\n    preference: Preference.PreferenceItem.SwitchPreference,\n    checked: Boolean,\n    onCheckChange: (Boolean) -> Unit\n) {\n    val isEnabled = LocalPreferenceEnabledStatus.current && preference.enabled\n\n    SwitchPreferenceContainer(\n        checked = checked,\n        isEnabled = isEnabled,\n        isPrimary = preference.primarySwitch\n    ) {\n        TextPreferenceWidget(\n            preference = preference,\n            onClick = { onCheckChange(!checked) }\n        ) {\n            Switch(\n                checked = checked,\n                onCheckedChange = onCheckChange,\n                enabled = isEnabled\n            )\n        }\n    }\n}\n\n@Composable\nprivate fun SwitchPreferenceContainer(\n    checked: Boolean,\n    isEnabled: Boolean,\n    isPrimary: Boolean,\n    content: @Composable () -> Unit\n) {\n    if (isPrimary) {\n        val containerColor = if (checked) {\n            MaterialTheme.colorScheme.primaryContainer\n        } else {\n            MaterialTheme.colorScheme.surfaceVariant\n        }\n\n        val tonalElevation = if (checked && isEnabled) 8.dp else 0.dp\n\n        Surface(\n            color = containerColor,\n            tonalElevation = tonalElevation,\n            shape = MaterialTheme.shapes.extraLarge,\n            modifier = Modifier.padding(MaterialTheme.spacing.medium),\n            content = content\n        )\n    } else {\n        content()\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun SwitchPreferencePreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) checked: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            SwitchPreferenceWidget(\n                preference = Preference.PreferenceItem.SwitchPreference(\n                    request = PreferenceRequest(\n                        key = booleanPreferencesKey(\"switch_preference\"),\n                        defaultValue = false\n                    ),\n                    title = \"Switch Preference\",\n                    enabled = true,\n                    primarySwitch = true\n                ),\n                checked = checked,\n                onCheckChange = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/components/preferences/widgets/TextPreferenceWidget.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.components.preferences.widgets\n\nimport androidx.compose.foundation.clickable\nimport androidx.compose.material3.ListItem\nimport androidx.compose.material3.ListItemDefaults\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport com.infinitepower.newquiz.core.compose.preferences.LocalPreferenceEnabledStatus\nimport com.infinitepower.newquiz.core.ui.DisabledContentEmphasis\nimport com.infinitepower.newquiz.feature.settings.model.Preference\n\n@Composable\ninternal fun TextPreferenceWidget(\n    preference: Preference.PreferenceItem<*>,\n    summary: String? = null,\n    onClick: () -> Unit = {},\n    trailing: @Composable (() -> Unit)? = null\n) {\n    val isEnabled = LocalPreferenceEnabledStatus.current && preference.enabled\n\n    val summaryText = summary ?: preference.summary\n\n    DisabledContentEmphasis(enabled = isEnabled) {\n        if (summaryText == null) {\n            ListItem(\n                headlineContent = {\n                    Text(\n                        text = preference.title,\n                        maxLines = if (preference.singleLineTitle) 1 else Int.MAX_VALUE\n                    )\n                },\n                leadingContent = preference.icon,\n                modifier = Modifier.clickable(\n                    enabled = isEnabled,\n                    onClick = onClick\n                ),\n                trailingContent = trailing,\n                colors = listItemColors()\n            )\n        } else {\n            ListItem(\n                headlineContent = {\n                    Text(\n                        text = preference.title,\n                        maxLines = if (preference.singleLineTitle) 1 else Int.MAX_VALUE\n                    )\n                },\n                supportingContent = {\n                    Text(\n                        text = summaryText,\n                        maxLines = if (preference.singleLineSummary) 1 else Int.MAX_VALUE\n                    )\n                },\n                leadingContent = preference.icon,\n                modifier = Modifier.clickable(\n                    enabled = isEnabled,\n                    onClick = onClick\n                ),\n                trailingContent = trailing,\n                colors = listItemColors()\n            )\n        }\n    }\n}\n\n@Composable\ninternal fun TextPreferenceWidgetRes(\n    preference: Preference.PreferenceItem<*>,\n    summary: String? = null,\n    onClick: () -> Unit = { },\n    trailing: @Composable (() -> Unit)? = null\n) {\n    val isEnabled = LocalPreferenceEnabledStatus.current && preference.enabled\n\n    DisabledContentEmphasis(enabled = isEnabled) {\n        ListItem(\n            headlineContent = {\n                Text(\n                    text = preference.title,\n                    maxLines = if (preference.singleLineTitle) 1 else Int.MAX_VALUE\n                )\n            },\n            supportingContent = {\n                val text = summary ?: preference.summary\n                if (text != null) {\n                    Text(\n                        text = text,\n                        maxLines = if (preference.singleLineSummary) 1 else Int.MAX_VALUE\n                    )\n                }\n            },\n            leadingContent = preference.icon,\n            modifier = Modifier.clickable(\n                enabled = isEnabled,\n                onClick = onClick\n            ),\n            trailingContent = trailing,\n            colors = listItemColors()\n        )\n    }\n}\n\n@Composable\ninternal fun TextPreferenceWidget(\n    preference: Preference.PreferenceItem<*>,\n    summary: @Composable () -> Unit,\n    trailing: @Composable (() -> Unit)? = null,\n    onClick: () -> Unit = { }\n) {\n    val isEnabled = LocalPreferenceEnabledStatus.current && preference.enabled\n\n    DisabledContentEmphasis(enabled = isEnabled) {\n        ListItem(\n            headlineContent = {\n                Text(\n                    text = preference.title,\n                    maxLines = if (preference.singleLineTitle) 1 else Int.MAX_VALUE\n                )\n            },\n            supportingContent = summary,\n            leadingContent = preference.icon,\n            modifier = Modifier.clickable(\n                enabled = isEnabled,\n                onClick = onClick\n            ),\n            trailingContent = trailing,\n            colors = listItemColors()\n        )\n    }\n}\n\n@Composable\nprivate fun listItemColors() = ListItemDefaults.colors(\n    containerColor = Color.Transparent\n)\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/model/Preference.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.model\n\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.runtime.Composable\nimport com.infinitepower.newquiz.core.datastore.PreferenceRequest\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\n\n/**\n * The basic building block that represents an individual setting displayed to a user in the preference hierarchy.\n */\nsealed class Preference {\n    abstract val title: String\n    abstract val enabled: Boolean\n    abstract val visible: Boolean\n\n    /**\n     * A single [Preference] item\n     */\n    sealed class PreferenceItem<T> : Preference() {\n        abstract val summary: String?\n        abstract val singleLineTitle: Boolean\n        abstract val singleLineSummary: Boolean\n\n        /**\n         * Represents the keys of a SwitchPreference that controls the state of this Preference.\n         * When the corresponding switch is turned off, this Preference is disabled and is unable to be modified.\n         */\n        abstract val dependency: ImmutableList<PreferenceRequest<Boolean>>\n\n        abstract val icon: @Composable (() -> Unit)?\n\n        data class NavigationButton(\n            override val title: String,\n            override val summary: String? = null,\n            override val singleLineTitle: Boolean = true,\n            override val singleLineSummary: Boolean = false,\n            override val dependency: ImmutableList<PreferenceRequest<Boolean>> = persistentListOf(),\n            override val icon: @Composable (() -> Unit)? = null,\n            override val enabled: Boolean = true,\n            override val visible: Boolean = true,\n\n            val screenKey: ScreenKey,\n            val itemSelected: Boolean,\n            val onClick: () -> Unit\n        ) : PreferenceItem<String>()\n\n        /**\n         * \tA basic [PreferenceItem] that only displays text.\n         */\n        data class TextPreference(\n            override val title: String,\n            override val summary: String? = null,\n            override val singleLineTitle: Boolean = true,\n            override val singleLineSummary: Boolean = false,\n            override val dependency: ImmutableList<PreferenceRequest<Boolean>> = persistentListOf(),\n            override val icon: @Composable (() -> Unit)? = null,\n            override val enabled: Boolean = true,\n            override val visible: Boolean = true,\n\n            val onClick: () -> Unit = {}\n        ) : PreferenceItem<String>()\n\n        /**\n         * \tA [PreferenceItem] that provides a two-state toggleable option.\n         *\n         * \t@param onCheckChange Called when the switch is toggled, the value is the new state of the switch\n         * \tor the value of the dependency if the switch is disabled\n         * \t@param primarySwitch If true, the switch will be displayed with the primary color in the container\n         * \tand the container will have a tonal elevation and padding.\n         */\n        data class SwitchPreference(\n            val request: PreferenceRequest<Boolean>,\n            override val title: String,\n            override val summary: String? = null,\n            override val singleLineTitle: Boolean = true,\n            override val singleLineSummary: Boolean = false,\n            override val dependency: ImmutableList<PreferenceRequest<Boolean>> = persistentListOf(),\n            override val icon: @Composable (() -> Unit)? = null,\n            override val enabled: Boolean = true,\n            override val visible: Boolean = true,\n\n            val onCheckChange: (newValue: Boolean) -> Unit = {},\n            val primarySwitch: Boolean = false\n        ) : PreferenceItem<Boolean>()\n\n        /**\n         * \tA [PreferenceItem] that displays a list of entries as a dialog.\n         * \tOnly one entry can be selected at any given time.\n         */\n        data class ListPreference(\n            val request: PreferenceRequest<String>,\n            override val title: String,\n            override val summary: String? = null,\n            override val singleLineTitle: Boolean = true,\n            override val singleLineSummary: Boolean = false,\n            override val dependency: ImmutableList<PreferenceRequest<Boolean>> = persistentListOf(),\n            override val icon: @Composable (() -> Unit)? = null,\n            override val enabled: Boolean = true,\n            override val visible: Boolean = true,\n\n            val entries: Map<String, String>,\n            val onItemClick: (value: String) -> Unit = {}\n        ) : PreferenceItem<String>()\n\n        /**\n         * A [PreferenceItem] that displays a list of entries as a dialog.\n         * Multiple entries can be selected at the same time.\n         */\n        data class MultiSelectListPreference(\n            val request: PreferenceRequest<Set<String>>,\n            override val title: String,\n            override val summary: String? = null,\n            override val singleLineTitle: Boolean = true,\n            override val singleLineSummary: Boolean = false,\n            override val dependency: ImmutableList<PreferenceRequest<Boolean>> = persistentListOf(),\n            override val icon: @Composable (() -> Unit)? = null,\n            override val enabled: Boolean = true,\n            override val visible: Boolean = true,\n\n            val entries: Map<String, String>,\n        ) : PreferenceItem<Set<String>>()\n\n        /**\n         * A [PreferenceItem] that displays a seekBar and the currently selected value.\n         */\n        data class SeekBarPreference(\n            val request: PreferenceRequest<Int>,\n            override val title: String,\n            override val summary: String? = null,\n            override val singleLineTitle: Boolean = true,\n            override val singleLineSummary: Boolean = false,\n            override val dependency: ImmutableList<PreferenceRequest<Boolean>> = persistentListOf(),\n            override val icon: @Composable (() -> Unit)? = null,\n            override val enabled: Boolean = true,\n            override val visible: Boolean = true,\n\n            val valueRange: ClosedRange<Int>,\n            val steps: Int = 0,\n            val valueRepresentation: (Int) -> String = { it.toString() }\n        ) : PreferenceItem<Int>()\n\n        /**\n         * \tA [PreferenceItem] that displays a list of entries as a DropDownMenu.\n         * \tOnly one entry can be selected at any given time.\n         */\n        data class DropDownMenuPreference(\n            val request: PreferenceRequest<String>,\n            override val title: String,\n            override val summary: String? = null,\n            override val singleLineTitle: Boolean = true,\n            override val singleLineSummary: Boolean = false,\n            override val dependency: ImmutableList<PreferenceRequest<Boolean>> = persistentListOf(),\n            override val icon: @Composable (() -> Unit)? = null,\n            override val enabled: Boolean = true,\n            override val visible: Boolean = true,\n\n            val entries: Map<String, String>,\n        ) : PreferenceItem<String>()\n    }\n\n    /**\n     * A container for multiple [PreferenceItem]s\n     */\n    data class PreferenceGroup(\n        override val title: String,\n        override val enabled: Boolean = true,\n        override val visible: Boolean = true,\n\n        val preferenceItems: List<PreferenceItem<out Any>>\n    ) : Preference()\n\n    /**\n     * @param title in custom preference title will be the semantic description\n     */\n    data class CustomPreference(\n        override val title: String,\n        override val enabled: Boolean = true,\n        override val visible: Boolean = true,\n\n        val content: @Composable BoxScope.() -> Unit\n    ) : Preference()\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/model/ScreenKey.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.model\n\nenum class ScreenKey {\n    GENERAL,\n    MULTI_CHOICE_QUIZ,\n    WORDLE,\n    TRANSLATION,\n    ABOUT_AND_HELP,\n    ANALYTICS,\n    ANIMATIONS,\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/PreferenceScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens\n\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.LargeTopAppBar\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBarDefaults\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.unit.dp\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.compose.preferences.LocalPreferenceEnabledStatus\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.feature.settings.components.preferences.PreferenceGroupHeader\nimport com.infinitepower.newquiz.feature.settings.components.preferences.PreferenceItem\nimport com.infinitepower.newquiz.feature.settings.components.preferences.widgets.CustomPreferenceWidget\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport kotlinx.collections.immutable.ImmutableList\n\n@Composable\n@ExperimentalMaterial3Api\nfun PreferenceScreen(\n    modifier: Modifier = Modifier,\n    onBackClick: () -> Unit,\n    title: String,\n    items: ImmutableList<Preference>,\n    dataStoreManager: DataStoreManager,\n    isScreenExpanded: Boolean\n) {\n    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()\n\n    Scaffold(\n        topBar = {\n            LargeTopAppBar(\n                title = { Text(text = title) },\n                scrollBehavior = scrollBehavior,\n                navigationIcon = {\n                    if (!isScreenExpanded) {\n                        BackIconButton(onClick = onBackClick)\n                    }\n                }\n            )\n        },\n        modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)\n    ) { innerPadding ->\n        PreferenceScreen(\n            modifier = Modifier.padding(innerPadding),\n            items = items,\n            dataStoreManager = dataStoreManager\n        )\n    }\n}\n\n/**\n * Preference Screen composable which contains a list of [Preference] items\n * @param items [Preference] items which should be displayed on the preference screen. An item can be a single [PreferenceItem] or a group ([Preference.PreferenceGroup])\n * @param dataStoreManager a [DataStoreManager] responsible for the dataStore backing the preference screen\n * @param modifier [Modifier] to be applied to the preferenceScreen layout\n */\n@Composable\ninternal fun PreferenceScreen(\n    modifier: Modifier = Modifier,\n    items: ImmutableList<Preference>,\n    dataStoreManager: DataStoreManager,\n    contentPadding: PaddingValues = PaddingValues(0.dp)\n) {\n    val prefs by dataStoreManager.preferenceFlow.collectAsStateWithLifecycle(initialValue = null)\n\n    LazyColumn(\n        modifier = modifier,\n        contentPadding = contentPadding\n    ) {\n        items.forEach { preference ->\n            if (preference.visible) {\n                when (preference) {\n                    // Create Preference Group\n                    is Preference.PreferenceGroup -> {\n                        item {\n                            PreferenceGroupHeader(title = preference.title)\n                        }\n\n                        items(preference.preferenceItems) { item ->\n                            val enabled = preference.enabled && item.dependency.all { dependency ->\n                                prefs?.get(dependency.key) ?: dependency.defaultValue\n                            }\n\n                            CompositionLocalProvider(LocalPreferenceEnabledStatus provides enabled) {\n                                PreferenceItem(\n                                    item = item,\n                                    prefs = prefs,\n                                    dataStoreManager = dataStoreManager\n                                )\n                            }\n                        }\n\n                        item {\n                            Spacer(modifier = Modifier.height(16.dp))\n                        }\n                    }\n\n                    // Create Preference Item\n                    is Preference.PreferenceItem<*> -> item {\n                        val enabled = preference.enabled && preference.dependency.all { dependency ->\n                            prefs?.get(dependency.key) ?: dependency.defaultValue\n                        }\n\n                        CompositionLocalProvider(LocalPreferenceEnabledStatus provides enabled) {\n                            PreferenceItem(\n                                item = preference,\n                                prefs = prefs,\n                                dataStoreManager = dataStoreManager\n                            )\n                        }\n                    }\n\n                    is Preference.CustomPreference -> {\n                        item {\n                            CustomPreferenceWidget(preference = preference)\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/about_and_help/AboutAndHelpScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.about_and_help\n\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.AppNameWithLogo\nimport com.infinitepower.newquiz.feature.settings.components.AboutAndHelpButtons\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.feature.settings.screens.PreferenceScreen\nimport com.infinitepower.newquiz.feature.settings.util.datastore.rememberSettingsDataStoreManager\nimport kotlinx.collections.immutable.persistentListOf\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun AboutAndHelpScreen(\n    modifier: Modifier = Modifier,\n    onBackClick: () -> Unit,\n    isScreenExpanded: Boolean,\n) {\n    val dataStoreManager = rememberSettingsDataStoreManager()\n\n    val items = remember {\n        persistentListOf(\n            Preference.CustomPreference(\n                title = \"NewQuiz Logo\",\n                content = {\n                    Column(\n                        horizontalAlignment = Alignment.CenterHorizontally,\n                        modifier = Modifier.padding(MaterialTheme.spacing.medium)\n                    ) {\n                        AppNameWithLogo()\n                        Spacer(modifier = Modifier.height(MaterialTheme.spacing.extraLarge))\n                        AboutAndHelpButtons()\n                    }\n                }\n            )\n        )\n    }\n\n    PreferenceScreen(\n        modifier = modifier,\n        title = stringResource(id = R.string.about_and_help),\n        items = items,\n        dataStoreManager = dataStoreManager,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick\n    )\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun AboutAndHelpScreenPreview() {\n    NewQuizTheme {\n        Surface {\n            AboutAndHelpScreen(\n                modifier = Modifier.padding(16.dp),\n                isScreenExpanded = true,\n                onBackClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/animations/AnimationsScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.animations\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.feature.settings.screens.PreferenceScreen\nimport com.infinitepower.newquiz.feature.settings.util.datastore.rememberSettingsDataStoreManager\nimport kotlinx.collections.immutable.persistentListOf\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun AnimationsScreen(\n    modifier: Modifier = Modifier,\n    onBackClick: () -> Unit,\n    isScreenExpanded: Boolean,\n) {\n    val dataStoreManager = rememberSettingsDataStoreManager()\n\n    val items = persistentListOf(\n        Preference.PreferenceItem.SwitchPreference(\n            request = SettingsCommon.GlobalAnimationsEnabled,\n            title = stringResource(id = CoreR.string.animations_enabled),\n            primarySwitch = true\n        ),\n        Preference.PreferenceItem.SwitchPreference(\n            request = SettingsCommon.WordleAnimationsEnabled,\n            title = stringResource(id = CoreR.string.wordle_animations_enabled),\n            summary = stringResource(id = CoreR.string.wordle_animations_enabled_description),\n            dependency = persistentListOf(SettingsCommon.GlobalAnimationsEnabled)\n        ),\n        Preference.PreferenceItem.SwitchPreference(\n            request = SettingsCommon.MultiChoiceAnimationsEnabled,\n            title = stringResource(id = CoreR.string.multi_choice_animations_enabled),\n            summary = stringResource(id = CoreR.string.multi_choice_animations_enabled_description),\n            dependency = persistentListOf(SettingsCommon.GlobalAnimationsEnabled)\n        )\n    )\n\n    PreferenceScreen(\n        modifier = modifier,\n        title = stringResource(id = CoreR.string.animations),\n        items = items,\n        dataStoreManager = dataStoreManager,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick\n    )\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun AnimationsScreenPreview() {\n    NewQuizTheme {\n        Surface {\n            AnimationsScreen(\n                modifier = Modifier.padding(16.dp),\n                isScreenExpanded = true,\n                onBackClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/general/GeneralScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.general\n\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Analytics\nimport androidx.compose.material.icons.rounded.Animation\nimport androidx.compose.material.icons.rounded.ClearAll\nimport androidx.compose.material.icons.rounded.Delete\nimport androidx.compose.material.icons.rounded.Place\nimport androidx.compose.material.icons.rounded.Thermostat\nimport androidx.compose.material.icons.rounded.Visibility\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.feature.settings.common.BuildVariant\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.feature.settings.model.ScreenKey\nimport com.infinitepower.newquiz.feature.settings.screens.PreferenceScreen\nimport com.infinitepower.newquiz.feature.settings.util.datastore.rememberSettingsDataStoreManager\nimport com.infinitepower.newquiz.feature.settings.util.getShowCategoryConnectionInfoEntryMap\nimport com.infinitepower.newquiz.model.regional_preferences.DistanceUnitType\nimport com.infinitepower.newquiz.model.regional_preferences.TemperatureUnit\nimport kotlinx.collections.immutable.persistentListOf\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun GeneralScreen(\n    modifier: Modifier = Modifier,\n    isScreenExpanded: Boolean,\n    onBackClick: () -> Unit,\n    navigateToScreen: (key: ScreenKey) -> Unit,\n    viewModel: GeneralScreenViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    GeneralScreen(\n        modifier = modifier,\n        uiState = uiState,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick,\n        navigateToScreen = navigateToScreen,\n        onEvent = viewModel::onEvent\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun GeneralScreen(\n    modifier: Modifier = Modifier,\n    uiState: GeneralScreenUiState = GeneralScreenUiState(),\n    onEvent: (GeneralScreenUiEvent) -> Unit,\n    isScreenExpanded: Boolean,\n    onBackClick: () -> Unit,\n    navigateToScreen: (key: ScreenKey) -> Unit\n) {\n    val dataStoreManager = rememberSettingsDataStoreManager()\n    val scope = rememberCoroutineScope()\n\n    val items = persistentListOf(\n        Preference.PreferenceItem.TextPreference(\n            title = stringResource(id = R.string.clear_settings),\n            summary = stringResource(id = R.string.remove_all_saved_settings),\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.Delete,\n                    contentDescription = stringResource(id = R.string.clear_settings)\n                )\n            },\n            enabled = true,\n            onClick = {\n                scope.launch(Dispatchers.IO) { dataStoreManager.clearPreferences() }\n            }\n        ),\n        Preference.PreferenceGroup(\n            title = stringResource(id = R.string.categories),\n            preferenceItems = listOf(\n                Preference.PreferenceItem.ListPreference(\n                    title = stringResource(id = R.string.show_category_connection_info),\n                    request = SettingsCommon.CategoryConnectionInfoBadge(\n                        default = uiState.defaultShowCategoryConnectionInfo\n                    ),\n                    entries = getShowCategoryConnectionInfoEntryMap(),\n                ),\n                Preference.PreferenceItem.SwitchPreference(\n                    title = stringResource(id = R.string.hide_online_categories),\n                    summary = stringResource(id = R.string.hide_online_categories_description),\n                    request = SettingsCommon.HideOnlineCategories,\n                    icon = {\n                        Icon(\n                            imageVector = Icons.Rounded.Visibility,\n                            contentDescription = stringResource(id = R.string.hide_online_categories)\n                        )\n                    }\n                ),\n                Preference.PreferenceItem.TextPreference(\n                    title = stringResource(id = R.string.clear_recent_categories),\n                    summary = stringResource(id = R.string.clear_recent_categories_description),\n                    onClick = { onEvent(GeneralScreenUiEvent.ClearHomeRecentCategories) },\n                    icon = {\n                        Icon(\n                            imageVector = Icons.Rounded.ClearAll,\n                            contentDescription = stringResource(id = R.string.clear_recent_categories),\n                        )\n                    }\n                )\n            )\n        ),\n        Preference.PreferenceGroup(\n            title = stringResource(id = R.string.regional_preferences),\n            preferenceItems = listOf(\n                Preference.PreferenceItem.ListPreference(\n                    title = stringResource(id = R.string.temperature_unit),\n                    request = SettingsCommon.TemperatureUnit,\n                    entries = mapOf(\n                        \"\" to stringResource(id = R.string.system_default),\n                        TemperatureUnit.CELSIUS.name to stringResource(id = R.string.celsius),\n                        TemperatureUnit.FAHRENHEIT.name to stringResource(id = R.string.fahrenheit),\n                    ),\n                    icon = {\n                        Icon(\n                            imageVector = Icons.Rounded.Thermostat,\n                            contentDescription = stringResource(id = R.string.temperature_unit)\n                        )\n                    }\n                ),\n                Preference.PreferenceItem.ListPreference(\n                    title = stringResource(id = R.string.distance_unit),\n                    request = SettingsCommon.DistanceUnitType,\n                    entries = mapOf(\n                        \"\" to stringResource(id = R.string.system_default),\n                        DistanceUnitType.METRIC.name to stringResource(id = R.string.metric),\n                        DistanceUnitType.IMPERIAL.name to stringResource(id = R.string.imperial),\n                    ),\n                    icon = {\n                        Icon(\n                            imageVector = Icons.Rounded.Place,\n                            contentDescription = stringResource(id = R.string.distance_unit)\n                        )\n                    }\n                ),\n            )\n        ),\n        Preference.PreferenceGroup(\n            title = stringResource(id = R.string.maze),\n            preferenceItems = listOf(\n                Preference.PreferenceItem.SwitchPreference(\n                    title = stringResource(id = R.string.maze_settings_auto_scroll_title),\n                    summary = stringResource(id = R.string.maze_settings_auto_scroll_summary),\n                    request = SettingsCommon.MazeAutoScrollToCurrentItem,\n                )\n            )\n        ),\n        Preference.PreferenceItem.TextPreference(\n            title = stringResource(id = R.string.animations),\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.Animation,\n                    contentDescription = stringResource(id = R.string.animations)\n                )\n            },\n            onClick = { navigateToScreen(ScreenKey.ANIMATIONS) }\n        ),\n        Preference.PreferenceItem.TextPreference(\n            title = stringResource(id = R.string.analytics),\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.Analytics,\n                    contentDescription = stringResource(id = R.string.analytics)\n                )\n            },\n            onClick = { navigateToScreen(ScreenKey.ANALYTICS) },\n            visible = BuildVariant.DISTRIBUTION_FLAVOR == \"normal\"\n        )\n    )\n\n    PreferenceScreen(\n        modifier = modifier,\n        title = \"General\",\n        items = items,\n        dataStoreManager = dataStoreManager,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick\n    )\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun GeneralScreenPreview() {\n    NewQuizTheme {\n        Surface {\n            GeneralScreen(\n                uiState = GeneralScreenUiState(),\n                isScreenExpanded = false,\n                onBackClick = {},\n                navigateToScreen = {},\n                onEvent = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/general/GeneralScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.general\n\ninterface GeneralScreenUiEvent {\n    data object ClearHomeRecentCategories : GeneralScreenUiEvent\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/general/GeneralScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.general\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\n\n@Keep\ndata class GeneralScreenUiState(\n    val defaultShowCategoryConnectionInfo: ShowCategoryConnectionInfo = ShowCategoryConnectionInfo.NONE\n)\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/general/GeneralScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.general\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\nclass GeneralScreenViewModel @Inject constructor(\n    private val recentCategoriesRepository: RecentCategoriesRepository,\n) : ViewModel() {\n    private val _uiState = MutableStateFlow(GeneralScreenUiState())\n    val uiState = _uiState.asStateFlow()\n\n    init {\n        _uiState.update { currentState ->\n            val defaultShowCategoryConnectionInfo = recentCategoriesRepository.getDefaultShowCategoryConnectionInfo()\n\n            currentState.copy(\n                defaultShowCategoryConnectionInfo = defaultShowCategoryConnectionInfo,\n            )\n        }\n    }\n\n    fun onEvent(event: GeneralScreenUiEvent){\n        when(event) {\n            is GeneralScreenUiEvent.ClearHomeRecentCategories -> {\n                viewModelScope.launch {\n                    recentCategoriesRepository.cleanAllSavedCategories()\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/main/MainScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.main\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.automirrored.rounded.Help\nimport androidx.compose.material.icons.automirrored.rounded.ListAlt\nimport androidx.compose.material.icons.rounded.Password\nimport androidx.compose.material.icons.rounded.Settings\nimport androidx.compose.material.icons.rounded.Translate\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.LargeTopAppBar\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBarDefaults\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.feature.settings.common.BuildVariant\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.feature.settings.model.ScreenKey\nimport com.infinitepower.newquiz.feature.settings.screens.PreferenceScreen\nimport com.infinitepower.newquiz.feature.settings.util.datastore.rememberSettingsDataStoreManager\nimport kotlinx.collections.immutable.persistentListOf\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun MainScreen(\n    modifier: Modifier = Modifier,\n    onBackClick: () -> Unit,\n    onScreenSelect: (key: ScreenKey) -> Unit,\n    currentScreen: ScreenKey?\n) {\n    val dataStoreManager = rememberSettingsDataStoreManager()\n\n    val items = persistentListOf(\n        // General\n        Preference.PreferenceItem.NavigationButton(\n            title = stringResource(id = CoreR.string.general),\n            summary = stringResource(id = CoreR.string.general_settings_summary),\n            singleLineSummary = true,\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.Settings,\n                    contentDescription = null\n                )\n            },\n            screenKey = ScreenKey.GENERAL,\n            itemSelected = currentScreen == ScreenKey.GENERAL,\n            onClick = { onScreenSelect(ScreenKey.GENERAL) },\n        ),\n        // Multi Choice Quiz\n        Preference.PreferenceItem.NavigationButton(\n            title = stringResource(id = CoreR.string.multi_choice_quiz),\n            summary = stringResource(id = CoreR.string.multi_choice_settings_summary),\n            singleLineSummary = true,\n            icon = {\n                Icon(\n                    imageVector = Icons.AutoMirrored.Rounded.ListAlt,\n                    contentDescription = null\n                )\n            },\n            screenKey = ScreenKey.MULTI_CHOICE_QUIZ,\n            itemSelected = currentScreen == ScreenKey.MULTI_CHOICE_QUIZ,\n            onClick = { onScreenSelect(ScreenKey.MULTI_CHOICE_QUIZ) },\n        ),\n        // Wordle\n        Preference.PreferenceItem.NavigationButton(\n            title = stringResource(id = CoreR.string.wordle),\n            summary = stringResource(id = CoreR.string.wordle_settings_summary),\n            singleLineSummary = true,\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.Password,\n                    contentDescription = null\n                )\n            },\n            screenKey = ScreenKey.WORDLE,\n            itemSelected = currentScreen == ScreenKey.WORDLE,\n            onClick = { onScreenSelect(ScreenKey.WORDLE) },\n        ),\n        // Translation\n        Preference.PreferenceItem.NavigationButton(\n            title = stringResource(id = CoreR.string.translation),\n            summary = stringResource(id = CoreR.string.translation_settings_summary),\n            singleLineSummary = true,\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.Translate,\n                    contentDescription = null\n                )\n            },\n            screenKey = ScreenKey.TRANSLATION,\n            itemSelected = currentScreen == ScreenKey.TRANSLATION,\n            onClick = { onScreenSelect(ScreenKey.TRANSLATION) },\n            visible = BuildVariant.DISTRIBUTION_FLAVOR == \"normal\"\n        ),\n        // About & Help\n        Preference.PreferenceItem.NavigationButton(\n            title = stringResource(id = CoreR.string.about_and_help),\n            summary = stringResource(id = CoreR.string.about_and_help_settings_summary),\n            singleLineSummary = true,\n            icon = {\n                Icon(\n                    imageVector = Icons.AutoMirrored.Rounded.Help,\n                    contentDescription = null\n                )\n            },\n            screenKey = ScreenKey.ABOUT_AND_HELP,\n            itemSelected = currentScreen == ScreenKey.ABOUT_AND_HELP,\n            onClick = { onScreenSelect(ScreenKey.ABOUT_AND_HELP) },\n        ),\n    )\n\n    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()\n\n    Scaffold(\n        topBar = {\n            LargeTopAppBar(\n                title = { Text(text = stringResource(id = CoreR.string.settings)) },\n                scrollBehavior = scrollBehavior,\n                navigationIcon = { BackIconButton(onClick = onBackClick) }\n            )\n        },\n        modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)\n    ) { innerPadding ->\n        PreferenceScreen(\n            modifier = Modifier.padding(innerPadding),\n            items = items,\n            dataStoreManager = dataStoreManager\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun MainScreenPreview() {\n    NewQuizTheme {\n        Surface {\n            MainScreen(\n                modifier = Modifier.padding(16.dp),\n                currentScreen = ScreenKey.GENERAL,\n                onScreenSelect = {},\n                onBackClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/multi_choice_quiz/MultiChoiceQuizScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.multi_choice_quiz\n\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.feature.settings.screens.PreferenceScreen\nimport com.infinitepower.newquiz.feature.settings.util.datastore.rememberSettingsDataStoreManager\nimport kotlinx.collections.immutable.persistentListOf\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun MultiChoiceQuizScreen(\n    modifier: Modifier = Modifier,\n    onBackClick: () -> Unit,\n    isScreenExpanded: Boolean,\n) {\n    val dataStoreManager = rememberSettingsDataStoreManager()\n\n    val items = persistentListOf(\n        Preference.PreferenceItem.SeekBarPreference(\n            request = SettingsCommon.MultiChoiceQuizQuestionsSize,\n            title = stringResource(id = CoreR.string.quiz_question_size),\n            singleLineTitle = true,\n            valueRange = (5..20)\n        )\n    )\n\n    PreferenceScreen(\n        modifier = modifier,\n        title = stringResource(id = CoreR.string.multi_choice_quiz),\n        items = items,\n        dataStoreManager = dataStoreManager,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick\n    )\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/screens/wordle/WordleScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.wordle\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Language\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.analytics.LocalAnalyticsHelper\nimport com.infinitepower.newquiz.core.analytics.UserProperty\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.feature.settings.screens.PreferenceScreen\nimport com.infinitepower.newquiz.feature.settings.util.datastore.rememberSettingsDataStoreManager\nimport kotlinx.collections.immutable.persistentListOf\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun WordleScreen(\n    modifier: Modifier = Modifier,\n    onBackClick: () -> Unit,\n    isScreenExpanded: Boolean,\n) {\n    val dataStoreManager = rememberSettingsDataStoreManager()\n\n    val analyticsHelper = LocalAnalyticsHelper.current\n\n    val items = persistentListOf(\n        Preference.PreferenceItem.SwitchPreference(\n            request = SettingsCommon.WordleHardMode,\n            title = stringResource(id = CoreR.string.hard_mode),\n            summary = stringResource(id = CoreR.string.any_revealed_hints_must_be_used_in_subsequest_guesses)\n        ),\n        Preference.PreferenceItem.SwitchPreference(\n            request = SettingsCommon.WordleColorBlindMode,\n            title = stringResource(id = CoreR.string.color_blind_mode),\n            summary = stringResource(id = CoreR.string.high_contrast_colors)\n        ),\n        Preference.PreferenceItem.SwitchPreference(\n            request = SettingsCommon.WordleLetterHints,\n            title = stringResource(id = CoreR.string.letter_hints),\n            summary = stringResource(\n                id = CoreR.string.hint_above_the_letter_that_it_appears_twice_or_more_in_the_hidden_word\n            )\n        ),\n        Preference.PreferenceGroup(\n            title = stringResource(id = CoreR.string.wordle_infinite),\n            preferenceItems = listOf(\n                Preference.PreferenceItem.ListPreference(\n                    request = SettingsCommon.InfiniteWordleQuizLanguage,\n                    title = stringResource(id = CoreR.string.quiz_language),\n                    icon = {\n                        Icon(\n                            imageVector = Icons.Rounded.Language,\n                            contentDescription = stringResource(id = CoreR.string.quiz_language),\n                        )\n                    },\n                    entries = mapOf(\n                        \"en\" to stringResource(id = CoreR.string.english),\n                        \"pt\" to stringResource(id = CoreR.string.portuguese),\n                        \"es\" to stringResource(id = CoreR.string.spanish),\n                        \"fr\" to stringResource(id = CoreR.string.french)\n                    ),\n                    onItemClick = { lang ->\n                        analyticsHelper.setUserProperty(UserProperty.WordleLanguage(lang))\n                    }\n                ),\n                Preference.PreferenceItem.SwitchPreference(\n                    request = SettingsCommon.WordleInfiniteRowsLimited,\n                    title = stringResource(id = CoreR.string.rows_limited),\n                    summary = stringResource(id = CoreR.string.wordle_infinite_row_limited)\n                ),\n                Preference.PreferenceItem.SeekBarPreference(\n                    request = SettingsCommon.WordleInfiniteRowsLimit,\n                    title = stringResource(id = CoreR.string.rows_limited),\n                    summary = stringResource(id = CoreR.string.wordle_infinite_row_limit_value),\n                    valueRange = 2..30,\n                    dependency = persistentListOf(SettingsCommon.WordleInfiniteRowsLimited)\n                )\n            )\n        )\n    )\n\n    PreferenceScreen(\n        modifier = modifier,\n        title = stringResource(id = CoreR.string.wordle),\n        items = items,\n        dataStoreManager = dataStoreManager,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick\n    )\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun WordleScreenPreview() {\n    NewQuizTheme {\n        Surface {\n            WordleScreen(\n                modifier = Modifier.padding(16.dp),\n                isScreenExpanded = true,\n                onBackClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/util/ShowCategoryConnectionInfoUtils.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.util\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.ReadOnlyComposable\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@ReadOnlyComposable\ninternal fun ShowCategoryConnectionInfo.getTitle(): UiText {\n    return when (this) {\n        ShowCategoryConnectionInfo.NONE -> UiText.StringResource(CoreR.string.none)\n        ShowCategoryConnectionInfo.BOTH -> UiText.StringResource(CoreR.string.both)\n        ShowCategoryConnectionInfo.REQUIRE_CONNECTION -> UiText.StringResource(CoreR.string.require_internet_connection)\n        ShowCategoryConnectionInfo.DONT_REQUIRE_CONNECTION -> {\n            UiText.StringResource(CoreR.string.dont_require_internet_connection)\n        }\n    }\n}\n\n@Composable\n@ReadOnlyComposable\ninternal fun getShowCategoryConnectionInfoEntryMap(): Map<String, String> {\n    return ShowCategoryConnectionInfo.entries.associate { info ->\n        info.name to info.getTitle().asString()\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/main/kotlin/com/infinitepower/newquiz/feature/settings/util/datastore/DatastoreUtils.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.util.datastore\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.platform.LocalContext\nimport com.infinitepower.newquiz.core.datastore.common.settingsDataStore\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.PreferencesDatastoreManager\n\n@Composable\nfun rememberSettingsDataStoreManager(): DataStoreManager {\n    val context = LocalContext.current\n    val dataStore = context.settingsDataStore\n\n    return remember {\n        PreferencesDatastoreManager(dataStore)\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/normal/kotlin/com/infinitepower/newquiz/feature/settings/common/BuildVariant.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.common\n\nobject BuildVariant {\n    internal const val DISTRIBUTION_FLAVOR = \"normal\"\n}\n"
  },
  {
    "path": "feature/settings/src/normal/kotlin/com/infinitepower/newquiz/feature/settings/screens/PreferencesScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens\n\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport com.infinitepower.newquiz.feature.settings.model.ScreenKey\nimport com.infinitepower.newquiz.feature.settings.screens.about_and_help.AboutAndHelpScreen\nimport com.infinitepower.newquiz.feature.settings.screens.analytics.AnalyticsScreen\nimport com.infinitepower.newquiz.feature.settings.screens.animations.AnimationsScreen\nimport com.infinitepower.newquiz.feature.settings.screens.general.GeneralScreen\nimport com.infinitepower.newquiz.feature.settings.screens.multi_choice_quiz.MultiChoiceQuizScreen\nimport com.infinitepower.newquiz.feature.settings.screens.translation.TranslationScreen\nimport com.infinitepower.newquiz.feature.settings.screens.wordle.WordleScreen\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun PreferencesScreen(\n    modifier: Modifier = Modifier,\n    currentScreen: ScreenKey?,\n    isScreenExpanded: Boolean,\n    onBackClick: () -> Unit,\n    navigateToScreen: (key: ScreenKey) -> Unit,\n) {\n    when (currentScreen) {\n        null -> {}\n        ScreenKey.GENERAL -> GeneralScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n            navigateToScreen = navigateToScreen\n        )\n        ScreenKey.MULTI_CHOICE_QUIZ -> MultiChoiceQuizScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        ScreenKey.WORDLE -> WordleScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        ScreenKey.ABOUT_AND_HELP -> AboutAndHelpScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        ScreenKey.ANIMATIONS -> AnimationsScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        ScreenKey.ANALYTICS -> AnalyticsScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n        ScreenKey.TRANSLATION -> TranslationScreen(\n            modifier = modifier,\n            isScreenExpanded = isScreenExpanded,\n            onBackClick = onBackClick,\n        )\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/normal/kotlin/com/infinitepower/newquiz/feature/settings/screens/analytics/AnimationsScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.analytics\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Analytics\nimport androidx.compose.material.icons.rounded.BugReport\nimport androidx.compose.material.icons.rounded.MonitorHeart\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.analytics.LocalAnalyticsHelper\nimport com.infinitepower.newquiz.core.datastore.common.DataAnalyticsCommon\nimport com.infinitepower.newquiz.core.datastore.common.dataAnalyticsDataStore\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.PreferencesDatastoreManager\nimport com.infinitepower.newquiz.core.R as CoreR\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.feature.settings.screens.PreferenceScreen\nimport com.infinitepower.newquiz.model.DataAnalyticsConsentState\nimport kotlinx.collections.immutable.persistentListOf\nimport kotlinx.coroutines.launch\n\n@Composable\nfun rememberDataAnalyticsDataStoreManager(): DataStoreManager {\n    val context = LocalContext.current\n    val dataStore = context.dataAnalyticsDataStore\n\n    return remember {\n        PreferencesDatastoreManager(dataStore)\n    }\n}\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun AnalyticsScreen(\n    modifier: Modifier = Modifier,\n    isScreenExpanded: Boolean,\n    onBackClick: () -> Unit,\n) {\n    val dataStoreManager = rememberDataAnalyticsDataStoreManager()\n    val scope = rememberCoroutineScope()\n\n    val analyticsHelper = LocalAnalyticsHelper.current\n\n    val items = persistentListOf(\n        // Global analytics dependency\n        Preference.PreferenceItem.SwitchPreference(\n            request = DataAnalyticsCommon.GloballyAnalyticsCollectionEnabled,\n            title = stringResource(id = CoreR.string.analytics_collection_enabled),\n            onCheckChange = { enabled ->\n                scope.launch {\n                    enableLoggingAnalytics(\n                        enabled = enabled,\n                        dataAnalyticsDataStoreManager = dataStoreManager,\n                        analyticsHelper = analyticsHelper\n                    )\n                }\n            },\n            primarySwitch = true\n        ),\n        Preference.PreferenceItem.SwitchPreference(\n            request = DataAnalyticsCommon.GeneralAnalyticsEnabled,\n            title = stringResource(id = CoreR.string.general_analytics_enabled),\n            summary = stringResource(id = CoreR.string.general_analytics_description_enabled),\n            dependency = persistentListOf(DataAnalyticsCommon.GloballyAnalyticsCollectionEnabled),\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.Analytics,\n                    contentDescription = stringResource(id = CoreR.string.general_analytics_enabled)\n                )\n            },\n            onCheckChange = analyticsHelper::setGeneralAnalyticsEnabled\n        ),\n        Preference.PreferenceItem.SwitchPreference(\n            request = DataAnalyticsCommon.CrashlyticsEnabled,\n            title = stringResource(id = CoreR.string.crash_analytics_enabled),\n            summary = stringResource(id = CoreR.string.crash_analytics_description_enabled),\n            dependency = persistentListOf(DataAnalyticsCommon.GloballyAnalyticsCollectionEnabled),\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.BugReport,\n                    contentDescription = stringResource(id = CoreR.string.crash_analytics_enabled)\n                )\n            },\n            onCheckChange = analyticsHelper::setCrashlyticsEnabled\n        ),\n        Preference.PreferenceItem.SwitchPreference(\n            request = DataAnalyticsCommon.PerformanceMonitoringEnabled,\n            title = stringResource(id = CoreR.string.performance_monitoring_enabled),\n            summary = stringResource(id = CoreR.string.performance_monitoring_description_enabled),\n            dependency = persistentListOf(DataAnalyticsCommon.GloballyAnalyticsCollectionEnabled),\n            icon = {\n                Icon(\n                    imageVector = Icons.Rounded.MonitorHeart,\n                    contentDescription = stringResource(id = CoreR.string.performance_monitoring_enabled)\n                )\n            },\n            onCheckChange = analyticsHelper::setPerformanceEnabled\n        )\n    )\n\n    PreferenceScreen(\n        modifier = modifier,\n        title = stringResource(id = CoreR.string.analytics),\n        items = items,\n        dataStoreManager = dataStoreManager,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick\n    )\n}\n\nprivate suspend fun enableLoggingAnalytics(\n    enabled: Boolean,\n    dataAnalyticsDataStoreManager: DataStoreManager,\n    analyticsHelper: AnalyticsHelper\n) {\n    val consentState = if (enabled) {\n        DataAnalyticsConsentState.AGREED\n    } else {\n        DataAnalyticsConsentState.DISAGREED\n    }\n\n    dataAnalyticsDataStoreManager.editPreference(\n        key = DataAnalyticsCommon.DataAnalyticsConsent.key,\n        newValue = consentState.name\n    )\n\n    // Enable general analytics\n    val generalEnabled = dataAnalyticsDataStoreManager.getPreference(DataAnalyticsCommon.GeneralAnalyticsEnabled)\n    analyticsHelper.setGeneralAnalyticsEnabled(generalEnabled && enabled)\n\n    // Enable crashlytics\n    val crashlyticsEnabled = dataAnalyticsDataStoreManager.getPreference(DataAnalyticsCommon.CrashlyticsEnabled)\n    analyticsHelper.setCrashlyticsEnabled(crashlyticsEnabled && enabled)\n\n    // Enable performance monitoring\n    val performanceMonitoringEnabled = dataAnalyticsDataStoreManager.getPreference(\n        DataAnalyticsCommon.PerformanceMonitoringEnabled\n    )\n    analyticsHelper.setPerformanceEnabled(performanceMonitoringEnabled && enabled)\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun AnalyticsScreenPreview() {\n    NewQuizTheme {\n        Surface {\n            AnalyticsScreen(\n                modifier = Modifier.padding(16.dp),\n                isScreenExpanded = true,\n                onBackClick = {}\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "feature/settings/src/normal/kotlin/com/infinitepower/newquiz/feature/settings/screens/translation/TranslationScreen.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.translation\n\nimport android.os.Build\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.datastore.common.TranslationCommon\nimport com.infinitepower.newquiz.core.R as CoreR\nimport com.infinitepower.newquiz.core.translation.TranslatorModelState\nimport com.infinitepower.newquiz.feature.settings.model.Preference\nimport com.infinitepower.newquiz.feature.settings.screens.PreferenceScreen\nimport com.infinitepower.newquiz.feature.settings.util.datastore.rememberSettingsDataStoreManager\nimport kotlinx.collections.immutable.persistentListOf\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun TranslationScreen(\n    modifier: Modifier = Modifier,\n    isScreenExpanded: Boolean,\n    onBackClick: () -> Unit,\n    viewModel: TranslationScreenViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    TranslationScreen(\n        modifier = modifier,\n        uiState = uiState,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick,\n        onEvent = viewModel::onEvent\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun TranslationScreen(\n    modifier: Modifier = Modifier,\n    uiState: TranslationScreenUiState,\n    isScreenExpanded: Boolean,\n    onBackClick: () -> Unit,\n    onEvent: (event: TranslationScreenUiEvent) -> Unit\n) {\n    val dataStoreManager = rememberSettingsDataStoreManager()\n\n    val items = persistentListOf(\n        Preference.PreferenceItem.SwitchPreference(\n            request = TranslationCommon.Enabled,\n            title = stringResource(id = CoreR.string.translation_enabled),\n            primarySwitch = true\n        ),\n        Preference.PreferenceItem.ListPreference(\n            request = TranslationCommon.TargetLanguage,\n            title = stringResource(id = CoreR.string.target_language),\n            summary = stringResource(id = CoreR.string.target_language_description),\n            entries = uiState.translatorTargetLanguages,\n            enabled = uiState.translationModelState == TranslatorModelState.None,\n            dependency = persistentListOf(TranslationCommon.Enabled)\n        ),\n        Preference.PreferenceItem.TextPreference(\n            title = stringResource(id = CoreR.string.download_translation_model),\n            summary = stringResource(id = CoreR.string.download_translation_model_description),\n            dependency = persistentListOf(TranslationCommon.Enabled),\n            visible = uiState.translationModelState == TranslatorModelState.None,\n            enabled = uiState.translatorTargetLanguage.isNotBlank(),\n            onClick = { onEvent(TranslationScreenUiEvent.DownloadTranslationModel) }\n        ),\n        Preference.PreferenceItem.TextPreference(\n            title = stringResource(id = CoreR.string.delete_translation_model),\n            dependency = persistentListOf(TranslationCommon.Enabled),\n            visible = uiState.translationModelState == TranslatorModelState.Downloaded,\n            onClick = { onEvent(TranslationScreenUiEvent.DeleteTranslationModel) }\n        ),\n        Preference.PreferenceGroup(\n            title = stringResource(id = CoreR.string.download_settings),\n            preferenceItems = listOf(\n                Preference.PreferenceItem.SwitchPreference(\n                    request = TranslationCommon.RequireWifi,\n                    title = stringResource(id = CoreR.string.require_wifi),\n                    summary = stringResource(id = CoreR.string.translation_require_wifi_description),\n                    dependency = persistentListOf(TranslationCommon.Enabled),\n                ),\n                Preference.PreferenceItem.SwitchPreference(\n                    request = TranslationCommon.RequireCharging,\n                    title = stringResource(id = CoreR.string.require_charging),\n                    summary = stringResource(id = CoreR.string.translation_require_charging_description),\n                    dependency = persistentListOf(TranslationCommon.Enabled),\n                    visible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N\n                ),\n            )\n        )\n    )\n\n    PreferenceScreen(\n        modifier = modifier,\n        title = stringResource(id = CoreR.string.translation),\n        items = items,\n        dataStoreManager = dataStoreManager,\n        isScreenExpanded = isScreenExpanded,\n        onBackClick = onBackClick\n    )\n}\n"
  },
  {
    "path": "feature/settings/src/normal/kotlin/com/infinitepower/newquiz/feature/settings/screens/translation/TranslationScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.translation\n\ninterface TranslationScreenUiEvent {\n    data object DownloadTranslationModel : TranslationScreenUiEvent\n\n    data object DeleteTranslationModel : TranslationScreenUiEvent\n}"
  },
  {
    "path": "feature/settings/src/normal/kotlin/com/infinitepower/newquiz/feature/settings/screens/translation/TranslationScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.translation\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.core.translation.TranslatorModelState\nimport com.infinitepower.newquiz.core.translation.TranslatorTargetLanguages\n\n@Keep\ndata class TranslationScreenUiState(\n    val translationModelState: TranslatorModelState = TranslatorModelState.None,\n    val translatorTargetLanguages: TranslatorTargetLanguages = emptyMap(),\n    val translatorTargetLanguage: String = \"\",\n)\n"
  },
  {
    "path": "feature/settings/src/normal/kotlin/com/infinitepower/newquiz/feature/settings/screens/translation/TranslationScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.feature.settings.screens.translation\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.core.datastore.common.TranslationCommon\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.translation.TranslatorModelState\nimport com.infinitepower.newquiz.core.translation.TranslatorUtil\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.catch\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\nimport kotlinx.coroutines.flow.stateIn\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\nclass TranslationScreenViewModel @Inject constructor(\n    private val translatorUtil: TranslatorUtil,\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager,\n) : ViewModel() {\n    private val _uiState = MutableStateFlow(TranslationScreenUiState())\n    val uiState = combine(\n        _uiState,\n        settingsDataStoreManager.getPreferenceFlow(TranslationCommon.TargetLanguage)\n    ) { uiState, targetLanguage ->\n        uiState.copy(\n            translatorTargetLanguage = targetLanguage\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(),\n        initialValue = TranslationScreenUiState()\n    )\n\n    init {\n        viewModelScope.launch {\n            _uiState.update { currentState ->\n                val translationModelState = if (translatorUtil.isModelDownloaded()) {\n                    TranslatorModelState.Downloaded\n                } else {\n                    TranslatorModelState.None\n                }\n\n                currentState.copy(\n                    translationModelState = translationModelState,\n                    translatorTargetLanguages = translatorUtil.availableTargetLanguages,\n                )\n            }\n        }\n    }\n\n    fun onEvent(event: TranslationScreenUiEvent) {\n        when (event) {\n            is TranslationScreenUiEvent.DownloadTranslationModel -> downloadTranslationModel()\n            is TranslationScreenUiEvent.DeleteTranslationModel -> {\n                viewModelScope.launch {\n                    translatorUtil.deleteModel()\n                }\n            }\n        }\n    }\n\n    private fun downloadTranslationModel() = viewModelScope.launch {\n        val targetLanguage = settingsDataStoreManager.getPreference(TranslationCommon.TargetLanguage)\n\n        // Check if the target language is picked by the user\n        if (targetLanguage.isEmpty()) {\n            return@launch\n        }\n\n        val requireWifi = settingsDataStoreManager.getPreference(TranslationCommon.RequireWifi)\n        val requireCharging = settingsDataStoreManager.getPreference(TranslationCommon.RequireCharging)\n\n        translatorUtil.downloadModel(\n            targetLanguage = targetLanguage,\n            requireWifi = requireWifi,\n            requireCharging = requireCharging\n        ).onEach { downloadState ->\n            _uiState.update { currentState ->\n                currentState.copy(\n                    translationModelState = downloadState\n                )\n            }\n        }.catch { exception ->\n            exception.printStackTrace()\n            _uiState.update { currentState ->\n                currentState.copy(\n                    translationModelState = TranslatorModelState.None\n                )\n            }\n        }.launchIn(viewModelScope)\n    }\n}\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\n\nandroidDesugarJdkLibs = \"2.1.3\"\nandroidGradlePlugin = \"8.7.3\"\nandroidxAnnotation = \"1.9.1\"\nandroidxActivity = \"1.9.3\"\nandroidxAppCompat = \"1.7.0\"\nandroidxCoreKtx = \"1.15.0\"\nandroidxCoreSplashscreen = \"1.0.1\"\nandroidxComposeCompiler = \"1.5.14\"\nandroidxComposeBom = \"2024.11.00\"\nandroidxDataStore = \"1.1.1\"\nandroidxStartup = \"1.2.0\"\nandroidxLifecycle = \"2.8.0\"\nandroidxWork = \"2.10.0\"\nandroidxTestRules = \"1.6.1\"\nandroidxTestRunner = \"1.6.1\"\nandroidxTracing = \"1.2.0\"\ncoil = \"2.7.0\"\ncomposeDestinations = \"1.11.7\"\nconstraintlayoutCompose = \"1.1.0\"\nfirebaseCrashlyticsPlugin = \"3.0.2\"\nfirebasePerfPlugin = \"1.4.2\"\ngmsPlugin = \"4.4.2\"\ngoogleMaterial = \"1.12.0\"\ngoogleTruth = \"1.4.4\"\ngoogleOss = \"17.1.0\"\ngoogleOssPlugin = \"0.10.6\"\ngraphicsShapes = \"1.0.1\"\nhilt = \"2.52\"\nhiltExt = \"1.2.0\"\nkotlin = \"2.0.21\"\nkotlinxCollectionsImmutable = \"0.3.8\"\nkotlinxCoroutines = \"1.9.0\"\nkotlinxDatetime = \"0.6.1\"\nkotlinxSerializationJson = \"1.7.3\"\nksp = \"2.0.21-1.0.28\"\nktor = \"2.3.12\"\nlottie = \"6.6.0\"\nfirebaseBom = \"33.6.0\"\nslf4j = \"2.0.16\"\njunitJupiter = \"5.11.3\"\nmockk = \"1.13.13\"\nroom = \"2.6.1\"\njavaxInject = \"1\"\nfirebaseUi = \"8.0.2\"\nvico = \"1.15.0\"\nmlKitTranslate = \"17.0.3\"\nturbine = \"1.2.0\"\ndetekt = \"1.23.7\"\ndetektCompose = \"0.4.19\"\njunit = \"4.13.2\"\njunitVersion = \"1.1.5\"\nespressoCore = \"3.5.1\"\n\n[libraries]\n\nandroid-desugarJdkLibs = { group = \"com.android.tools\", name = \"desugar_jdk_libs\", version.ref = \"androidDesugarJdkLibs\" }\nandroidx-annotation = { group = \"androidx.annotation\", name = \"annotation\", version.ref = \"androidxAnnotation\" }\nandroidx-activity-compose = { group = \"androidx.activity\", name = \"activity-compose\", version.ref = \"androidxActivity\" }\nandroidx-appcompat = { group = \"androidx.appcompat\", name = \"appcompat\", version.ref = \"androidxAppCompat\" }\nandroidx-constraintlayout-compose = { module = \"androidx.constraintlayout:constraintlayout-compose\", version.ref = \"constraintlayoutCompose\" }\nandroidx-compose-bom = { group = \"androidx.compose\", name = \"compose-bom\", version.ref = \"androidxComposeBom\" }\nandroidx-compose-foundation = { group = \"androidx.compose.foundation\", name = \"foundation\" }\nandroidx-compose-foundation-layout = { group = \"androidx.compose.foundation\", name = \"foundation-layout\" }\nandroidx-compose-material-iconsExtended = { group = \"androidx.compose.material\", name = \"material-icons-extended\" }\nandroidx-compose-material3 = { group = \"androidx.compose.material3\", name = \"material3\" }\nandroidx-compose-material3-windowSizeClass = { group = \"androidx.compose.material3\", name = \"material3-window-size-class\" }\nandroidx-compose-runtime = { group = \"androidx.compose.runtime\", name = \"runtime\" }\nandroidx-compose-runtime-livedata = { group = \"androidx.compose.runtime\", name = \"runtime-livedata\" }\nandroidx-compose-ui = { group = \"androidx.compose.ui\", name = \"ui\" }\nandroidx-compose-ui-test = { group = \"androidx.compose.ui\", name = \"ui-test-junit4\" }\nandroidx-compose-ui-testManifest = { group = \"androidx.compose.ui\", name = \"ui-test-manifest\" }\nandroidx-compose-ui-tooling = { group = \"androidx.compose.ui\", name = \"ui-tooling\" }\nandroidx-compose-ui-tooling-preview = { group = \"androidx.compose.ui\", name = \"ui-tooling-preview\" }\nandroidx-core-ktx = { group = \"androidx.core\", name = \"core-ktx\", version.ref = \"androidxCoreKtx\" }\nandroidx-core-splashscreen = { group = \"androidx.core\", name = \"core-splashscreen\", version.ref = \"androidxCoreSplashscreen\" }\nandroidx-dataStore-preferences = { group = \"androidx.datastore\", name = \"datastore-preferences\", version.ref = \"androidxDataStore\" }\nandroidx-graphics-shapes = { module = \"androidx.graphics:graphics-shapes\", version.ref = \"graphicsShapes\" }\nandroidx-startup-runtime = { group = \"androidx.startup\", name = \"startup-runtime\", version.ref = \"androidxStartup\" }\nandroidx-lifecycle-livedata-ktx = { group = \"androidx.lifecycle\", name = \"lifecycle-livedata-ktx\" }\nandroidx-lifecycle-runtime-ktx = { group = \"androidx.lifecycle\", name = \"lifecycle-runtime-ktx\" }\nandroidx-lifecycle-runtimeCompose = { group = \"androidx.lifecycle\", name = \"lifecycle-runtime-compose\" }\nandroidx-lifecycle-viewModelCompose = { group = \"androidx.lifecycle\", name = \"lifecycle-viewmodel-compose\" }\nandroidx-work-ktx = { group = \"androidx.work\", name = \"work-runtime-ktx\", version.ref = \"androidxWork\" }\nandroidx-work-testing = { group = \"androidx.work\", name = \"work-testing\", version.ref = \"androidxWork\" }\nandroidx-test-rules = { group = \"androidx.test\", name = \"rules\", version.ref = \"androidxTestRules\" }\nandroidx-test-runner = { group = \"androidx.test\", name = \"runner\", version.ref = \"androidxTestRunner\" }\nandroidx-tracing-ktx = { group = \"androidx.tracing\", name = \"tracing-ktx\", version.ref = \"androidxTracing\" }\ncoil-kt = { group = \"io.coil-kt\", name = \"coil\", version.ref = \"coil\" }\ncoil-kt-compose = { group = \"io.coil-kt\", name = \"coil-compose\", version.ref = \"coil\" }\ncoil-kt-svg = { group = \"io.coil-kt\", name = \"coil-svg\", version.ref = \"coil\" }\ncompose-destinations-animations-core = { group = \"io.github.raamcosta.compose-destinations\", name = \"animations-core\", version.ref = \"composeDestinations\" }\ncompose-destinations-core = { group = \"io.github.raamcosta.compose-destinations\", name = \"core\", version.ref = \"composeDestinations\" }\ncompose-destinations-ksp = { group = \"io.github.raamcosta.compose-destinations\", name = \"ksp\", version.ref = \"composeDestinations\" }\ngoogle-material = { group = \"com.google.android.material\", name = \"material\", version.ref = \"googleMaterial\" }\ngoogle-oss-licenses = { group = \"com.google.android.gms\", name = \"play-services-oss-licenses\", version.ref = \"googleOss\" }\ngoogle-oss-licenses-plugin = { group = \"com.google.android.gms\", name = \"oss-licenses-plugin\", version.ref = \"googleOssPlugin\" }\ngoogle-truth = { group = \"com.google.truth\", name = \"truth\", version.ref = \"googleTruth\" }\nhilt-android = { group = \"com.google.dagger\", name = \"hilt-android\", version.ref = \"hilt\" }\nhilt-android-testing = { group = \"com.google.dagger\", name = \"hilt-android-testing\", version.ref = \"hilt\" }\nhilt-compiler = { group = \"com.google.dagger\", name = \"hilt-android-compiler\", version.ref = \"hilt\" }\nhilt-ext-compiler = { group = \"androidx.hilt\", name = \"hilt-compiler\", version.ref = \"hiltExt\" }\nhilt-ext-work = { group = \"androidx.hilt\", name = \"hilt-work\", version.ref = \"hiltExt\" }\nhilt-navigationCompose = { group = \"androidx.hilt\", name = \"hilt-navigation-compose\", version.ref = \"hiltExt\" }\nkotlinx-collections-immutable = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-collections-immutable\", version.ref = \"kotlinxCollectionsImmutable\" }\nkotlinx-coroutines-core = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-coroutines-core\", version.ref = \"kotlinxCoroutines\" }\nkotlinx-coroutines-android = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-coroutines-android\", version.ref = \"kotlinxCoroutines\" }\nkotlinx-coroutines-playServices = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-coroutines-play-services\", version.ref = \"kotlinxCoroutines\" }\nkotlinx-coroutines-test = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-coroutines-test\", version.ref = \"kotlinxCoroutines\" }\nkotlinx-datetime = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-datetime\", version.ref = \"kotlinxDatetime\" }\nkotlinx-serialization-json = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-serialization-json\", version.ref = \"kotlinxSerializationJson\" }\nktor-client-core = { group = \"io.ktor\", name = \"ktor-client-core\", version.ref = \"ktor\" }\nktor-client-okhttp = { group = \"io.ktor\", name = \"ktor-client-okhttp\", version.ref = \"ktor\" }\nktor-client-serialization = { group = \"io.ktor\", name = \"ktor-client-serialization\", version.ref = \"ktor\" }\nktor-client-mock = { group = \"io.ktor\", name = \"ktor-client-mock\", version.ref = \"ktor\" }\nlottie-compose = { group = \"com.airbnb.android\", name = \"lottie-compose\", version.ref = \"lottie\" }\nfirebase-bom = { group = \"com.google.firebase\", name = \"firebase-bom\", version.ref = \"firebaseBom\" }\nfirebase-analytics = { group = \"com.google.firebase\", name = \"firebase-analytics\" }\nfirebase-remoteConfig = { group = \"com.google.firebase\", name = \"firebase-config\" }\nfirebase-crashlytics = { group = \"com.google.firebase\", name = \"firebase-crashlytics\" }\nfirebase-perf = { group = \"com.google.firebase\", name = \"firebase-perf\" }\nfirebase-auth = { group = \"com.google.firebase\", name = \"firebase-auth\" }\nfirebase-firestore = { group = \"com.google.firebase\", name = \"firebase-firestore\" }\nslf4j-simple = { group = \"org.slf4j\", name = \"slf4j-simple\", version.ref = \"slf4j\" }\njunit-jupiter = { group = \"org.junit.jupiter\", name = \"junit-jupiter-api\", version.ref = \"junitJupiter\" }\njunit-jupiter-params = { group = \"org.junit.jupiter\", name = \"junit-jupiter-params\", version.ref = \"junitJupiter\" }\nmockk = { group = \"io.mockk\", name = \"mockk\", version.ref = \"mockk\" }\nmockk-android = { group = \"io.mockk\", name = \"mockk-android\", version.ref = \"mockk\" }\nroom-compiler = { group = \"androidx.room\", name = \"room-compiler\", version.ref = \"room\" }\nroom-ktx = { group = \"androidx.room\", name = \"room-ktx\", version.ref = \"room\" }\nroom-runtime = { group = \"androidx.room\", name = \"room-runtime\", version.ref = \"room\" }\nroom-testing = { group = \"androidx.room\", name = \"room-testing\", version.ref = \"room\" }\njavax-inject = { group = \"javax.inject\", name = \"javax.inject\", version.ref = \"javaxInject\" }\nfirebaseUi-auth = { group = \"com.firebaseui\", name = \"firebase-ui-auth\", version.ref = \"firebaseUi\" }\nvico-compose = { group = \"com.patrykandpatryk.vico\", name = \"compose\", version.ref = \"vico\" }\nvico-compose-m3 = { group = \"com.patrykandpatryk.vico\", name = \"compose-m3\", version.ref = \"vico\" }\ngoogle-mlKit-translate = { group = \"com.google.mlkit\", name = \"translate\", version.ref = \"mlKitTranslate\" }\nturbine = { group = \"app.cash.turbine\", name = \"turbine\", version.ref = \"turbine\" }\ndetekt-compose = { group = \"io.nlopez.compose.rules\", name = \"detekt\", version.ref = \"detektCompose\" }\n\n# Dependencies of the included build-logic\nandroid-gradlePlugin = { group = \"com.android.tools.build\", name = \"gradle\", version.ref = \"androidGradlePlugin\" }\nfirebase-crashlytics-gradlePlugin = { group = \"com.google.firebase\", name = \"firebase-crashlytics-gradle\", version.ref = \"firebaseCrashlyticsPlugin\" }\nfirebase-performance-gradlePlugin = { group = \"com.google.firebase\", name = \"perf-plugin\", version.ref = \"firebasePerfPlugin\" }\nkotlin-gradlePlugin = { group = \"org.jetbrains.kotlin\", name = \"kotlin-gradle-plugin\", version.ref = \"kotlin\" }\nksp-gradlePlugin = { group = \"com.google.devtools.ksp\", name = \"com.google.devtools.ksp.gradle.plugin\", version.ref = \"ksp\" }\ndetekt-gradlePlugin = { group = \"io.gitlab.arturbosch.detekt\", name = \"detekt-gradle-plugin\", version.ref = \"detekt\" }\ncompose-gradlePlugin = { module = \"org.jetbrains.kotlin:compose-compiler-gradle-plugin\", version.ref = \"kotlin\" }\njunit = { group = \"junit\", name = \"junit\", version.ref = \"junit\" }\nandroidx-junit = { group = \"androidx.test.ext\", name = \"junit\", version.ref = \"junitVersion\" }\nandroidx-espresso-core = { group = \"androidx.test.espresso\", name = \"espresso-core\", version.ref = \"espressoCore\" }\n\n[plugins]\n\nandroid-application = { id = \"com.android.application\", version.ref = \"androidGradlePlugin\" }\nandroid-library = { id = \"com.android.library\", version.ref = \"androidGradlePlugin\" }\ncompose-compiler = { id = \"org.jetbrains.kotlin.plugin.compose\", version.ref = \"kotlin\" }\nkotlin-jvm = { id = \"org.jetbrains.kotlin.jvm\", version.ref = \"kotlin\" }\nkotlin-serialization = { id = \"org.jetbrains.kotlin.plugin.serialization\", version.ref = \"kotlin\" }\nfirebase-crashlytics = { id = \"com.google.firebase.crashlytics\", version.ref = \"firebaseCrashlyticsPlugin\" }\nfirebase-perf = { id = \"com.google.firebase.firebase-perf\", version.ref = \"firebasePerfPlugin\" }\ngms = { id = \"com.google.gms.google-services\", version.ref = \"gmsPlugin\" }\nhilt = { id = \"com.google.dagger.hilt.android\", version.ref = \"hilt\" }\nksp = { id = \"com.google.devtools.ksp\", version.ref = \"ksp\" }\ndetekt = { id = \"io.gitlab.arturbosch.detekt\", version.ref = \"detekt\" }\n\nnewquiz-android-application = { id = \"newquiz.android.application\" }\nnewquiz-android-application-compose = { id = \"newquiz.android.application.compose\" }\nnewquiz-android-feature = { id = \"newquiz.android.feature\" }\nnewquiz-android-hilt = { id = \"newquiz.android.hilt\" }\nnewquiz-android-compose-destinations = { id = \"newquiz.android.compose.destinations\" }\nnewquiz-android-library = { id = \"newquiz.android.library\" }\nnewquiz-android-library-compose = { id = \"newquiz.android.library.compose\" }\nnewquiz-android-room = { id = \"newquiz.android.room\" }\nnewquiz-kotlin-serialization = { id = \"newquiz.kotlin.serialization\" }\nnewquiz-jvm-library = { id = \"newquiz.jvm.library\" }\nnewquiz-detekt = { id = \"newquiz.detekt\" }\n#jetbrains-kotlin-android = { id = \"org.jetbrains.kotlin.android\" }\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Jun 03 19:47:53 WEST 2024\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.11.1-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xms1024m -Xmx4096m -Dfile.encoding=UTF-8 -XX:+UseParallelGC\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=false\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n\n# Enable Gradle Daemon\norg.gradle.daemon=true\n\n# Enable R8 full mode\nandroid.enableR8.fullMode = true\n\n# Turn on parallel compilation, caching and on-demand configuration\norg.gradle.configureondemand=true\norg.gradle.caching=true\norg.gradle.parallel=true\n\nkotlin.incremental=true\n\norg.gradle.unsafe.configuration-cache=true\nandroid.nonTransitiveRClass=true\nandroid.nonFinalResIds=false\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n    <!-- Generated by `./gradlew refreshVersions` to avoid errors when using _ as a version. -->\n    <issue id=\"GradlePluginVersion\" severity=\"ignore\" />\n    <issue id=\"GradleDependency\" severity=\"ignore\" />\n</lint>"
  },
  {
    "path": "model/.gitignore",
    "content": "/build"
  },
  {
    "path": "model/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.jvm.library)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n    alias(libs.plugins.newquiz.detekt)\n}\n\ndependencies {\n    testImplementation(libs.google.truth)\n    testImplementation(libs.junit.jupiter)\n    testImplementation(libs.mockk)\n\n    implementation(libs.androidx.annotation)\n\n    implementation(libs.kotlinx.coroutines.core)\n    implementation(libs.kotlinx.datetime)\n\n    api(libs.kotlinx.collections.immutable)\n}\n\ntasks.withType<Test> {\n    useJUnitPlatform()\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/BaseCategory.kt",
    "content": "package com.infinitepower.newquiz.model\n\ninterface BaseCategory : GameModeCategory {\n    val id: String\n\n    val name: UiText\n\n    val image: Any\n\n    val requireInternetConnection: Boolean\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/DataAnalyticsConsentState.kt",
    "content": "package com.infinitepower.newquiz.model\n\nenum class DataAnalyticsConsentState {\n    NONE,\n    AGREED,\n    DISAGREED\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/GameMode.kt",
    "content": "package com.infinitepower.newquiz.model\n\n/**\n * This enum class is used to determine which game mode the user is playing.\n */\nenum class GameMode {\n    /**\n     * Multiple choice game mode.\n     */\n    MULTI_CHOICE,\n\n    /**\n     * Wordle game mode.\n     */\n    WORDLE,\n\n    /**\n     * Comparison game mode.\n     */\n    COMPARISON_QUIZ\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/GameModeCategory.kt",
    "content": "package com.infinitepower.newquiz.model\n\ninterface GameModeCategory {\n    val gameMode: GameMode\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/Language.kt",
    "content": "package com.infinitepower.newquiz.model\n\n@JvmInline\nvalue class Language(val value: String) {\n    companion object {\n        val ENGLISH = Language(\"en\")\n    }\n\n    init {\n        require(value.isNotEmpty()) { \"Language value must not be empty\" }\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/NumberFormatType.kt",
    "content": "package com.infinitepower.newquiz.model\n\n/**\n * This enum class is used to define the number format type.\n */\nenum class NumberFormatType {\n    /**\n     * Default number format type, no conversion is done.\n     */\n    DEFAULT,\n\n    /**\n     * Converts the number in milliseconds to a date.\n     */\n    DATE,\n\n    /**\n     * Converts the number in milliseconds to a time.\n     */\n    TIME,\n\n    /**\n     * Converts the number in milliseconds to a date and time.\n     */\n    DATETIME,\n\n    /**\n     * Converts the number to a percentage.\n     */\n    PERCENTAGE,\n\n    /**\n     * Converts the number to a temperature.\n     * The number is converted to a temperature unit based on the locale\n     * or the settings configuration.\n     */\n    TEMPERATURE,\n\n    /**\n     * Converts the number to a distance.\n     * The number is converted to a distance unit based on the locale\n     * or the settings configuration.\n     */\n    DISTANCE,\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/RemainingTime.kt",
    "content": "package com.infinitepower.newquiz.model\n\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.milliseconds\n\n@JvmInline\nvalue class RemainingTime constructor(val value: Duration) {\n    companion object {\n        val ZERO = RemainingTime(Duration.ZERO)\n\n        /**\n         * Creates a new instance of [RemainingTime] from the given milliseconds value.\n         */\n        fun fromMilliseconds(millis: Long): RemainingTime {\n            require(millis >= 0L) {\n                \"RemainingTime value must be greater than or equal to 0\"\n            }\n            return RemainingTime(millis.milliseconds)\n        }\n\n        private const val SECONDS_IN_MINUTE = 60\n    }\n\n    init {\n        require(value >= Duration.ZERO) { \"RemainingTime value must be greater than 0\" }\n    }\n\n    fun isZero(): Boolean = value == Duration.ZERO\n\n    /**\n     * @return the remaining percentage of the time.\n     * @param maxTime in milliseconds\n     */\n    fun getRemainingPercent(maxTime: Duration): Double = value / maxTime\n\n    /**\n     * @return the remaining time in minute:second format.\n     */\n    fun toMinuteSecondFormatted(): String {\n        val minutes = value.inWholeMinutes\n        val seconds = value.inWholeSeconds % SECONDS_IN_MINUTE\n        return if (minutes == 0L) seconds.toString() else \"$minutes:$seconds\"\n    }\n\n    /**\n     * Returns the elapsed seconds from the max time.\n     * @param maxTime in milliseconds\n     */\n    fun getElapsedSeconds(maxTime: Duration): Long = maxTime.inWholeSeconds - value.inWholeSeconds\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/Resource.kt",
    "content": "package com.infinitepower.newquiz.model\n\nimport kotlinx.coroutines.flow.Flow\n\nsealed class Resource<T>(\n    val data: T? = null,\n    val message: String? = null\n) {\n    class Success<T>(data: T) : Resource<T>(data)\n\n    class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)\n\n    class Loading<T>(data: T? = null) : Resource<T>(data)\n\n    override fun toString(): String {\n        return when (this) {\n            is Success -> \"Success with data: $data\"\n            is Error -> \"Error: $message, with data: $data\"\n            is Loading -> \"Loading with data $data\"\n        }\n    }\n\n    fun isSuccess(): Boolean = this is Success\n\n    fun isLoading(): Boolean = this is Loading\n\n    fun isError(): Boolean = this is Error\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (other !is Resource<*>) return false\n\n        return this.data == other.data && this.message == other.message\n    }\n\n    override fun hashCode(): Int {\n        var result = data?.hashCode() ?: 0\n        result = 31 * result + (message?.hashCode() ?: 0)\n        return result\n    }\n}\n\ntypealias FlowResource<T> = Flow<Resource<T>>\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/TimestampWithValue.kt",
    "content": "package com.infinitepower.newquiz.model\n\ndata class TimestampWithValue <T> (\n    val timestamp: Long,\n    val value: T\n)\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/UiText.kt",
    "content": "package com.infinitepower.newquiz.model\n\nimport androidx.annotation.PluralsRes\nimport androidx.annotation.StringRes\n\nsealed interface UiText {\n    val args: Array<out Any>\n\n    override fun toString(): String\n\n    class DynamicString(\n        val value: String,\n        override vararg val args: Any\n    ) : UiText {\n        override fun toString(): String = value.format(args.toList().toTypedArray())\n    }\n\n    class StringResource(\n        @StringRes val resId: Int,\n        override vararg val args: Any\n    ) : UiText {\n        override fun toString(): String {\n            return \"String resource: $resId with args: ${args.joinToString()}\"\n        }\n    }\n\n    class PluralStringResource(\n        @PluralsRes val resId: Int,\n        val quantity: Int,\n        override vararg val args: Any\n    ) : UiText {\n        override fun toString(): String {\n            return \"Plural string resource: $resId with quantity: $quantity and args: ${args.joinToString()}\"\n        }\n    }\n}\n\nfun String.toUiText(\n    vararg args: Any\n): UiText = UiText.DynamicString(value = this, args = args)\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/XP.kt",
    "content": "package com.infinitepower.newquiz.model\n\ntypealias XP = Int\n\ntypealias TimestampWithXP = TimestampWithValue<XP>\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/category/ShowCategoryConnectionInfo.kt",
    "content": "package com.infinitepower.newquiz.model.category\n\nimport androidx.annotation.Keep\n\n/**\n * Enum class that represents the possible values for the category connection info badge.\n * The badge is shown on the category card in the home screen.\n *\n * This is used in an entry of the settings screen.\n */\n@Keep\nenum class ShowCategoryConnectionInfo {\n    /**\n     * Don't show the badge.\n     */\n    NONE,\n\n    /**\n     * Show the badge if the category requires internet connection or not.\n     */\n    BOTH,\n\n    /**\n     * Show the badge only if the category requires internet connection.\n     */\n    REQUIRE_CONNECTION,\n\n    /**\n     * Show the badge only if the category doesn't require internet connection.\n     */\n    DONT_REQUIRE_CONNECTION;\n\n    /**\n     * Returns true if the badge should be shown for the category.\n     *\n     * @param requireInternetConnection Whether the category requires internet connection.\n     * @return True if the badge should be shown for the category.\n     */\n    fun shouldShowBadge(\n        requireInternetConnection: Boolean,\n    ): Boolean {\n        return when (this) {\n            NONE -> false\n            BOTH -> true\n            REQUIRE_CONNECTION -> requireInternetConnection\n            DONT_REQUIRE_CONNECTION -> !requireInternetConnection\n        }\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonMode.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\n/**\n * Represents the comparison mode for a quiz question.\n */\nenum class ComparisonMode { GREATER, LESSER }\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonQuizCategory.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.UiText\nimport kotlinx.serialization.Serializable\n\n/**\n * A category of comparison quizzes.\n * @param id The id of the category.\n * @param name The title of the category.\n * @param image The url of the image of the category.\n * @param isMazeDisabled Whether the category is not available for the maze.\n * @param questionDescription The description of the question.\n * @param helperValueSuffix The suffix of the question value.\n * @param generateQuestionsLocally Whether the questions should be generated locally.\n */\n@Keep\n@Serializable\ndata class ComparisonQuizCategory(\n    override val id: String,\n    override val name: UiText,\n    override val image: String,\n    override val requireInternetConnection: Boolean = true,\n    val generateQuestionsLocally: Boolean = false,\n    val isMazeDisabled: Boolean = false,\n    val description: String,\n    val questionDescription: QuestionDescription,\n    val formatType: NumberFormatType,\n    val helperValueSuffix: String? = null,\n    val dataSourceAttribution: DataSourceAttribution? = null\n) : BaseCategory, java.io.Serializable, Comparable<ComparisonQuizCategory> {\n    override val gameMode: GameMode = GameMode.COMPARISON_QUIZ\n\n    fun getQuestionDescription(\n        comparisonMode: ComparisonMode\n    ): String = when (comparisonMode) {\n        ComparisonMode.GREATER -> questionDescription.greater\n        ComparisonMode.LESSER -> questionDescription.less\n    }\n\n    override fun compareTo(other: ComparisonQuizCategory): Int {\n        return id.compareTo(other.id)\n    }\n\n    override fun equals(other: Any?): Boolean {\n        // The categories are equal if they have the same id.\n        return other is ComparisonQuizCategory && id == other.id\n    }\n\n    override fun hashCode(): Int {\n        return id.hashCode()\n    }\n\n    @Keep\n    @Serializable\n    data class QuestionDescription(\n        val greater: String,\n        val less: String\n    ) : java.io.Serializable\n\n    /**\n     * The data source attribution of the category.\n     */\n    @Keep\n    @Serializable\n    data class DataSourceAttribution(\n        val text: String,\n        val logo: String? = null\n    ) : java.io.Serializable\n\n    fun toEntity(): ComparisonQuizCategoryEntity = ComparisonQuizCategoryEntity(\n        id = id,\n        name = name.toString(),\n        image = image,\n        requireInternetConnection = requireInternetConnection,\n        generateQuestionsLocally = generateQuestionsLocally,\n        description = description,\n        questionDescription = questionDescription,\n        formatType = formatType.name.lowercase(),\n        helperValueSuffix = helperValueSuffix,\n        dataSourceAttribution = dataSourceAttribution\n    )\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonQuizCategoryEntity.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.toUiText\nimport kotlinx.serialization.Serializable\n\n@Keep\n@Serializable\ndata class ComparisonQuizCategoryEntity(\n    val id: String,\n    val name: String,\n    val image: String,\n    val requireInternetConnection: Boolean = true,\n    val generateQuestionsLocally: Boolean = false,\n    val description: String,\n    val questionDescription: ComparisonQuizCategory.QuestionDescription,\n    val formatType: String,\n    val helperValueSuffix: String? = null,\n    val dataSourceAttribution: ComparisonQuizCategory.DataSourceAttribution? = null\n) : java.io.Serializable {\n    fun toModel(): ComparisonQuizCategory = ComparisonQuizCategory(\n        id = id,\n        name = name.toUiText(),\n        image = image,\n        requireInternetConnection = requireInternetConnection,\n        generateQuestionsLocally = generateQuestionsLocally,\n        description = description,\n        questionDescription = questionDescription,\n        formatType = NumberFormatType.valueOf(formatType.uppercase()),\n        helperValueSuffix = helperValueSuffix,\n        dataSourceAttribution = dataSourceAttribution\n    )\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonQuizHelperValueState.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\n/**\n * Represents the state of the helper value for the comparison quiz item.\n * @see ComparisonQuizItem\n */\nenum class ComparisonQuizHelperValueState {\n    /**\n     * The helper value is hidden.\n     */\n    HIDDEN,\n\n    /**\n     * The helper value is visible and has a normal state.\n     */\n    NORMAL\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonQuizItem.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\nimport androidx.annotation.Keep\nimport java.net.URI\n\n@Keep\ndata class ComparisonQuizItem(\n    val title: String,\n    val value: Double,\n    val imgUri: URI\n)\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonQuizItemEntity.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\nimport androidx.annotation.Keep\nimport kotlinx.serialization.Serializable\n\n@Keep\n@Serializable\ndata class ComparisonQuizItemEntity(\n    val title: String,\n    val value: Double = 0.0,\n    val imgUrl: String\n) : java.io.Serializable\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonQuizQuestion.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\ntypealias QuestionPair = Pair<ComparisonQuizItem, ComparisonQuizItem>\n\n/**\n * Represents a question in the comparison quiz.\n * @param questions Pair of questions to compare.\n * @see ComparisonQuizItem\n */\ndata class ComparisonQuizQuestion(\n    val questions: QuestionPair,\n    val categoryId: String,\n//    val category: ComparisonQuizCategory,\n    val comparisonMode: ComparisonMode\n) {\n    /**\n     * Returns true if the [answer] is the correct answer for the [comparisonMode].\n     * If the values are equal, the answer is considered correct.\n     *\n     * @param answer The user selected answer to check.\n     * @return True if the [answer] is the correct answer for the [comparisonMode].\n     * @see ComparisonMode\n     * @see ComparisonQuizItem\n     */\n    fun isCorrectAnswer(answer: ComparisonQuizItem): Boolean {\n        val correctValue = when (comparisonMode) {\n            ComparisonMode.GREATER -> maxOf(questions.first.value, questions.second.value)\n            ComparisonMode.LESSER -> minOf(questions.first.value, questions.second.value)\n        }\n\n        return answer.value == correctValue\n    }\n\n    /**\n     * Returns a new [ComparisonQuizQuestion] with the second question of the [questions] as the first one and the [newQuestion] as the second one.\n     * @param newQuestion New second question.\n     * @return New [ComparisonQuizQuestion] with the second question of the [questions] as the first one.\n     * @see ComparisonQuizItem\n     */\n    fun nextQuestion(newQuestion: ComparisonQuizItem): ComparisonQuizQuestion {\n        return copy(questions = questions.second to newQuestion)\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/country/Continent.kt",
    "content": "package com.infinitepower.newquiz.model.country\n\n@JvmInline\nvalue class Continent private constructor(\n    val name: String\n) {\n    companion object {\n        private val Africa = Continent(\"Africa\")\n        private val Asia = Continent(\"Asia\")\n        private val Europe = Continent(\"Europe\")\n        private val NorthAmerica = Continent(\"North America\")\n        private val SouthAmerica = Continent(\"South America\")\n        private val Oceania = Continent(\"Oceania\")\n\n        private val allContinents = listOf(\n            Africa,\n            Asia,\n            Europe,\n            NorthAmerica,\n            SouthAmerica,\n            Oceania\n        )\n\n        fun from(name: String): Continent = allContinents\n            .firstOrNull { it.name == name }\n            ?: throw IllegalArgumentException(\"Unknown continent with name: $name\")\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/country/Country.kt",
    "content": "package com.infinitepower.newquiz.model.country\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport java.net.URI\n\n/**\n * Data class representing a country.\n *\n * @property countryCode The alpha-2 country code.\n * @property countryName The country name.\n * @property capital The capital of the country.\n * @property population The population of the country.\n * @property area The area of the country, in square kilometers.\n * @property continent The continent the country is in.\n * @property difficulty The difficulty of the question.\n * @property flagImage The flag image of the country.\n */\n@Keep\ndata class Country(\n    val countryCode: String,\n    val countryName: String,\n    val capital: String,\n    val population: Long,\n    val area: Double,\n    val continent: Continent,\n    val difficulty: QuestionDifficulty,\n    val flagImage: URI\n)\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/daily_challenge/DailyChallengeTask.kt",
    "content": "package com.infinitepower.newquiz.model.daily_challenge\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport kotlinx.datetime.Clock\nimport kotlinx.datetime.Instant\nimport kotlinx.datetime.TimeZone\nimport kotlinx.datetime.toLocalDateTime\nimport kotlin.time.Duration.Companion.days\n\n/**\n * A challenge task.\n *\n * @property id The task's id.\n * @property diamondsReward The number of diamonds rewarded for completing the task.\n * @property experienceReward The number of experience points rewarded for completing the task.\n * @property isClaimed Whether the task has been claimed.\n * @property dateRange The date range in which the task is valid.\n * @property currentValue The current value of the task.\n * @property maxValue The maximum value of the task.\n *\n */\n@Keep\ndata class DailyChallengeTask(\n    val id: Int,\n    val title: UiText,\n    val diamondsReward: UInt,\n    val experienceReward: UInt,\n    val isClaimed: Boolean,\n    val dateRange: ClosedRange<Instant>,\n    val currentValue: UInt,\n    val maxValue: UInt,\n    val event: GameEvent\n) {\n    init {\n        require(dateRange.start <= dateRange.endInclusive) {\n            val tz = TimeZone.currentSystemDefault()\n            val start = dateRange.start.toLocalDateTime(tz).date\n            val end = dateRange.endInclusive.toLocalDateTime(tz).date\n\n            \"The start date ($start) must be less than or equal to the end date ($end).\"\n        }\n    }\n\n    /**\n     * A task is daily if the [dateRange] is one day long.\n     */\n    fun isDaily(): Boolean {\n        val start = dateRange.start\n        val end = dateRange.endInclusive\n\n        return start == end - 1.days\n    }\n\n    /**\n     * A task is weekly if the [dateRange] is seven days long.\n     */\n    fun isWeekly(): Boolean {\n        val start = dateRange.start\n        val end = dateRange.endInclusive\n\n        return start == end - 7.days\n    }\n\n    /**\n     * A task is expired if the current time is not in the task's date range.\n     * The function compares the current time to the task's date range.\n     *\n     * @return true if the task is expired, false otherwise.\n     */\n    fun isExpired(): Boolean {\n        val now = Clock.System.now()\n\n        return now !in dateRange\n    }\n\n    /**\n     * A task is completed if the current [currentValue] is equal to the [maxValue].\n     *\n     * @return true if the task is completed, false otherwise.\n     */\n    fun isCompleted(): Boolean = currentValue >= maxValue\n\n    /**\n     * A task is claimable if it is completed and not claimed.\n     *\n     * @return true if the task is claimable, false otherwise.\n     */\n    fun isClaimable(): Boolean = !isExpired() && isCompleted() && !isClaimed\n\n    /**\n     * A task is active if it is not expired and not claimed.\n     *\n     * @return true if the task is active, false otherwise.\n     */\n    fun isActive(): Boolean = !isExpired() && !isClaimed\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (other !is DailyChallengeTask) return false\n\n        val dateRangeEquals = dateRange.start.toEpochMilliseconds() == other.dateRange.start.toEpochMilliseconds()\n                && dateRange.endInclusive.toEpochMilliseconds() == other.dateRange.endInclusive.toEpochMilliseconds()\n\n        return id == other.id\n                && diamondsReward == other.diamondsReward\n                && experienceReward == other.experienceReward\n                && isClaimed == other.isClaimed\n                && dateRangeEquals\n                && currentValue == other.currentValue\n                && maxValue == other.maxValue\n                && event == other.event\n    }\n\n    override fun hashCode(): Int {\n        var result = id\n        result = 31 * result + diamondsReward.hashCode()\n        result = 31 * result + experienceReward.hashCode()\n        result = 31 * result + isClaimed.hashCode()\n        result = 31 * result + dateRange.hashCode()\n        result = 31 * result + currentValue.hashCode()\n        result = 31 * result + maxValue.hashCode()\n        result = 31 * result + event.hashCode()\n        return result\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/global_event/GameEvent.kt",
    "content": "package com.infinitepower.newquiz.model.global_event\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceCategory\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport kotlin.random.Random\n\nsealed class GameEvent(\n    val key: String,\n    val valueRange: UIntProgression\n) {\n    override fun toString(): String = key\n\n    companion object {\n        private fun eventsWithoutArgs(): List<GameEvent> = listOf(\n            // Multi choice\n            MultiChoice.PlayRandomQuiz,\n            MultiChoice.EndQuiz,\n            MultiChoice.PlayQuestions,\n            MultiChoice.GetAnswersCorrect,\n            // Wordle\n            Wordle.GetWordCorrect\n        )\n\n        fun fromKey(key: String): GameEvent {\n            val eventsWithoutArgs = eventsWithoutArgs()\n\n            return when {\n                // Multi choice play quiz with category\n                key.startsWith(MultiChoice.PlayQuizWithCategory.KEY_PREFIX) -> {\n                    val categoryKey = key.substringAfter(MultiChoice.PlayQuizWithCategory.KEY_PREFIX)\n\n                    MultiChoice.PlayQuizWithCategory(categoryKey)\n                }\n                // Wordle play word with category\n                key.startsWith(Wordle.PlayWordWithCategory.KEY_PREFIX) -> {\n                    val categoryKey = key.substringAfter(Wordle.PlayWordWithCategory.KEY_PREFIX)\n\n                    Wordle.PlayWordWithCategory(WordleQuizType.valueOf(categoryKey))\n                }\n                // Comparison quiz play quiz with category\n                key.startsWith(ComparisonQuiz.PlayQuizWithCategory.KEY_PREFIX) -> {\n                    val categoryId = key.substringAfter(ComparisonQuiz.PlayQuizWithCategory.KEY_PREFIX)\n\n                    ComparisonQuiz.PlayQuizWithCategory(categoryId)\n                }\n                // Comparison quiz play quiz with mode\n                key.startsWith(ComparisonQuiz.PlayWithComparisonMode.KEY_PREFIX) -> {\n                    val modeId = key.substringAfter(ComparisonQuiz.PlayWithComparisonMode.KEY_PREFIX)\n\n                    ComparisonQuiz.PlayWithComparisonMode(ComparisonMode.valueOf(modeId))\n                }\n                // Comparison quiz play and get score\n                key.startsWith(ComparisonQuiz.PlayAndGetScore.KEY_PREFIX) -> {\n                    val score = key.substringAfter(ComparisonQuiz.PlayAndGetScore.KEY_PREFIX).toInt()\n\n                    ComparisonQuiz.PlayAndGetScore(score)\n                }\n                // Other events without args\n                eventsWithoutArgs.any { it.key == key } -> eventsWithoutArgs.first { it.key == key }\n                else -> throw IllegalArgumentException(\"Unknown key: $key\")\n            }\n        }\n\n        fun getRandomEvents(\n            count: Int,\n            multiChoiceCategories: List<MultiChoiceCategory>,\n            comparisonQuizCategories: List<ComparisonQuizCategory>,\n            random: Random = Random\n        ): List<GameEvent> {\n            val multiChoicePlayQuizWithCategory = MultiChoice.PlayQuizWithCategory(\n                categoryId = multiChoiceCategories.random(random).id\n            )\n\n            val wordleWithType = Wordle.PlayWordWithCategory(\n                wordleCategory = WordleQuizType.entries.random(random)\n            )\n\n            val comparisonPlayWithCategory = ComparisonQuiz.PlayQuizWithCategory(\n                categoryId = comparisonQuizCategories.random(random).id\n            )\n\n            val comparisonPlayWithMode = ComparisonQuiz.PlayWithComparisonMode(\n                mode = ComparisonMode.entries.random(random)\n            )\n\n            val comparisonPlayAndGetScore = ComparisonQuiz.PlayAndGetScore(\n                score = random.nextInt(from = 5, until = 20)\n            )\n\n            val allTypes = eventsWithoutArgs() + listOf(\n                multiChoicePlayQuizWithCategory,\n                wordleWithType,\n                comparisonPlayWithCategory,\n                comparisonPlayWithMode,\n                comparisonPlayAndGetScore\n            )\n\n            return allTypes.shuffled(random).take(count)\n        }\n    }\n\n    sealed class MultiChoice(\n        key: String,\n        taskValueRange: UIntProgression\n    ) : GameEvent(key, taskValueRange) {\n        object PlayRandomQuiz : MultiChoice(\n            key = \"multi_choice_play_random_quiz\",\n            taskValueRange = 1u..5u\n        )\n\n        object EndQuiz : MultiChoice(\n            key = \"multi_choice_end_quiz\",\n            taskValueRange = 1u..5u\n        )\n\n        @Keep\n        data class PlayQuizWithCategory(\n            val categoryId: String\n        ) : MultiChoice(\n            key = \"$KEY_PREFIX$categoryId\",\n            taskValueRange = 1u..5u\n        ) {\n            companion object {\n                internal const val KEY_PREFIX = \"multi_choice_play_quiz_category:\"\n            }\n        }\n\n        object PlayQuestions : MultiChoice(\n            key = \"multi_choice_play_questions\",\n            taskValueRange = 10u..50u step 10\n        )\n\n        object GetAnswersCorrect : MultiChoice(\n            key = \"multi_choice_get_questions_correct\",\n            taskValueRange = 5u..20u step 5\n        )\n    }\n\n    sealed class Wordle(\n        key: String,\n        taskValueRange: UIntProgression\n    ) : GameEvent(key, taskValueRange) {\n        object GetWordCorrect : Wordle(\n            key = \"wordle_get_word_correct\",\n            taskValueRange = 1u..5u\n        )\n\n        @Keep\n        data class PlayWordWithCategory(\n            val wordleCategory: WordleQuizType\n        ) : Wordle(\n            key = \"$KEY_PREFIX${wordleCategory.name}\",\n            taskValueRange = 1u..5u\n        ) {\n            companion object {\n                internal const val KEY_PREFIX = \"wordle_word_play_with_category:\"\n            }\n        }\n    }\n\n    sealed class ComparisonQuiz(\n        key: String,\n        taskValueRange: UIntProgression\n    ) : GameEvent(key, taskValueRange) {\n        @Keep\n        data class PlayQuizWithCategory(\n            val categoryId: String\n        ) : ComparisonQuiz(\n            key = \"$KEY_PREFIX$categoryId\",\n            taskValueRange = 1u..5u\n        ) {\n            companion object {\n                internal const val KEY_PREFIX = \"comparison_play_with_category:\"\n            }\n        }\n\n        @Keep\n        data class PlayAndGetScore(\n            val score: Int\n        ) : ComparisonQuiz(\n            key = \"$KEY_PREFIX$score\",\n            taskValueRange = 1u..5u\n        ) {\n            companion object {\n                internal const val KEY_PREFIX = \"comparison_play_and_get_score:\"\n            }\n        }\n\n        @Keep\n        data class PlayWithComparisonMode(\n            val mode: ComparisonMode\n        ) : ComparisonQuiz(\n            key = \"$KEY_PREFIX${mode.name}\",\n            taskValueRange = 1u..5u\n        ) {\n            companion object {\n                internal const val KEY_PREFIX = \"comparison_play_with_comparison_mode:\"\n            }\n        }\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (other !is GameEvent) return false\n\n        return key == other.key\n    }\n\n    override fun hashCode(): Int {\n        return key.hashCode()\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/math_quiz/MathFormula.kt",
    "content": "package com.infinitepower.newquiz.model.math_quiz\n\nimport androidx.annotation.Keep\n\n@Keep\ndata class MathFormula(\n    val leftFormula: String,\n    val solution: Int\n) {\n    val fullFormula: String\n        get() = \"$leftFormula=$solution\"\n\n    override fun toString(): String = fullFormula\n\n    companion object {\n        val mathFormulaRegex = \"^-*(\\\\d+[-+*/])+\\\\d+\\\\=-*\\\\d+\\$\".toRegex()\n\n        fun fromStringFullFormula(value: String): MathFormula = MathFormula(\n            leftFormula = value.takeWhile { it != '=' },\n            solution = value\n                .takeLastWhile { it != '=' }\n                .toIntOrNull() ?: error(\"Invalid MathFormula: $value\")\n        )\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/maze/MazePoint.kt",
    "content": "package com.infinitepower.newquiz.model.maze\n\nimport androidx.annotation.Keep\nimport kotlin.math.pow\n\n@Keep\ndata class MazePoint(\n    val x: Float,\n    val y: Float\n) {\n    override fun toString(): String = \"x: $x, y: $y\"\n}\n\n/**\n * Checks if this point is inside the circle defined by [center] and [radius], using the\n * Pythagorean theorem.\n *\n * @param center the center point of the circle\n * @param radius the radius of the circle\n * @return true if this point is inside the circle defined by [center] and [radius]\n */\nfun MazePoint.isInsideCircle(\n    center: MazePoint,\n    radius: Float\n): Boolean {\n    val dx = x - center.x\n    val dy = y - center.y\n    return dx.pow(2) + dy.pow(2) <= radius.pow(2)\n}\n\n/**\n * Creates a maze like this:\n *\n *       ()\n *       |\n *       ()---|\n *            |\n *  |---()---()\n *  |\n *  ()---()---|\n *            |\n *  ()---()---()\n */\nfun generateMazePointsTopToBottom(\n    startPoint: MazePoint,\n    increment: MazePoint\n): Sequence<MazePoint> = sequence {\n    yield(MazePoint(x = startPoint.x, y = startPoint.y))\n\n    var yValue = startPoint.y + increment.y\n\n    while (true) {\n        yield(MazePoint(x = startPoint.x, y = yValue))\n        yield(MazePoint(x = startPoint.x + increment.x, y = yValue))\n        yValue += increment.y\n        yield(MazePoint(x = startPoint.x + increment.x, y = yValue))\n        yield(MazePoint(x = startPoint.x, y = yValue))\n        yield(MazePoint(x = startPoint.x - increment.x, y = yValue))\n        yValue += increment.y\n        yield(MazePoint(x = startPoint.x - increment.x, y = yValue))\n    }\n}\n\n/**\n * Creates a maze like this:\n *\n *  ()---()---()\n *            |\n *  ()---()---|\n *  |\n *  |---()---()\n *           |\n *      ()---|\n *      |\n *     ()\n */\nfun generateMazePointsBottomToTop(\n    startPoint: MazePoint,\n    increment: MazePoint\n): Sequence<MazePoint> = sequence {\n    yield(MazePoint(x = startPoint.x, y = startPoint.y))\n\n    var yValue = startPoint.y - increment.y\n\n    while (true) {\n        yield(MazePoint(x = startPoint.x, y = yValue))\n        yield(MazePoint(x = startPoint.x + increment.x, y = yValue))\n        yValue -= increment.y\n        yield(MazePoint(x = startPoint.x + increment.x, y = yValue))\n        yield(MazePoint(x = startPoint.x, y = yValue))\n        yield(MazePoint(x = startPoint.x - increment.x, y = yValue))\n        yValue -= increment.y\n        yield(MazePoint(x = startPoint.x - increment.x, y = yValue))\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/maze/MazeQuiz.kt",
    "content": "package com.infinitepower.newquiz.model.maze\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.comparison_quiz.ComparisonQuizQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleWord\nimport kotlinx.collections.immutable.ImmutableList\nimport kotlinx.collections.immutable.persistentListOf\n\n@Keep\ndata class MazeQuiz(\n    val items: ImmutableList<MazeItem>\n) {\n    sealed interface MazeItem {\n        val id: Int\n        val mazeSeed: Int\n        val difficulty: QuestionDifficulty\n        val played: Boolean\n        val gameMode: GameMode\n        val categoryId: String\n\n        @Keep\n        data class Wordle(\n            val wordleWord: WordleWord,\n            val wordleQuizType: WordleQuizType,\n            override val id: Int = 0,\n            override val mazeSeed: Int,\n            override val difficulty: QuestionDifficulty = QuestionDifficulty.Easy,\n            override val played: Boolean = false,\n        ) : MazeItem {\n            override val gameMode: GameMode = GameMode.WORDLE\n\n            override val categoryId: String = wordleQuizType.name\n        }\n\n        @Keep\n        data class MultiChoice(\n            val question: MultiChoiceQuestion,\n            override val id: Int = 0,\n            override val mazeSeed: Int,\n            override val difficulty: QuestionDifficulty = QuestionDifficulty.Easy,\n            override val played: Boolean = false\n        ) : MazeItem {\n            override val gameMode: GameMode = GameMode.MULTI_CHOICE\n\n            override val categoryId: String = question.category.id\n        }\n\n        @Keep\n        data class ComparisonQuiz(\n            val question: ComparisonQuizQuestion,\n            override val id: Int = 0,\n            override val mazeSeed: Int,\n            override val difficulty: QuestionDifficulty = QuestionDifficulty.Easy,\n            override val played: Boolean = false\n        ) : MazeItem {\n            override val gameMode: GameMode = GameMode.COMPARISON_QUIZ\n\n            override val categoryId: String = question.categoryId\n        }\n    }\n}\n\nfun emptyMaze(): MazeQuiz = MazeQuiz(items = persistentListOf())\n\n/**\n * Check if an item at a given index in a list of MazeItem objects is playable.\n * An item is considered playable if it has not been played and the previous item has been played.\n *\n * @param index The index of the item to check.\n * @return true if the current item has not been played and either the previous item does not exist or has been played\n */\nfun List<MazeQuiz.MazeItem>.isPlayableItem(index: Int): Boolean {\n    // Check if the current item has been played. If it has, return false.\n    if (isItemPlayed(index)) return false\n\n    // If the current item is the first item in the list, return true.\n    if (index == 0) return true\n\n    // If the current item is not the first item in the list, check if the previous item has been played.\n    // If it has, return true. Otherwise, return false.\n    return isItemPlayed(index - 1)\n}\n\n/**\n * Returns true if the item at the given index has been played, false otherwise.\n *\n * @param index The index of the item to check.\n * @return True if the item has been played, false otherwise.\n */\ninfix fun List<MazeQuiz.MazeItem>.isItemPlayed(\n    index: Int\n): Boolean = getOrNull(index)?.played == true\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/MultiChoiceBaseCategory.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz\n\nimport kotlinx.serialization.KSerializer\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.descriptors.PrimitiveKind\nimport kotlinx.serialization.descriptors.PrimitiveSerialDescriptor\nimport kotlinx.serialization.descriptors.SerialDescriptor\nimport kotlinx.serialization.encoding.Decoder\nimport kotlinx.serialization.encoding.Encoder\n\n@Serializable(with = MultiChoiceBaseCategorySerializer::class)\nsealed class MultiChoiceBaseCategory(\n    val id: String,\n) : java.io.Serializable {\n    companion object {\n        fun fromId(id: String) = when (id) {\n            Logo.id -> Logo\n            Flag.id -> Flag\n            GuessMathSolution.id -> GuessMathSolution\n            NumberTrivia.id -> NumberTrivia\n            CountryCapitalFlags.id -> CountryCapitalFlags\n            else -> Normal(id)\n        }\n    }\n\n    override fun toString(): String = id\n\n    val hasCategory: Boolean\n        get() = id.isNotBlank() && id != \"random\"\n\n    /**\n     * Random multi choice category using [Normal] class\n     */\n    object Random : Normal()\n\n    /**\n     * Normal multi choice type with category\n     * @param categoryId category to the quiz\n     */\n    open class Normal(\n        val categoryId: String\n    ) : MultiChoiceBaseCategory(id = categoryId) {\n        /** Sets multi choice type as no category */\n        constructor() : this(\"random\")\n\n        override fun toString(): String = categoryId\n\n        override fun equals(other: Any?): Boolean {\n            if (other !is Normal) return false\n\n            return this.categoryId == other.categoryId\n        }\n\n        override fun hashCode(): Int = categoryId.hashCode()\n    }\n\n    /** Logo multi choice quiz category */\n    object Logo : MultiChoiceBaseCategory(id = \"logo\")\n\n    /** Flag multi choice quiz category */\n    object Flag : MultiChoiceBaseCategory(id = \"flag\")\n\n    /** Number trivia multi choice quiz category */\n    object CountryCapitalFlags : MultiChoiceBaseCategory(id = \"country_capital_flags\")\n\n    /** Guess math solution multi choice quiz category */\n    object GuessMathSolution : MultiChoiceBaseCategory(id = \"guess_math_solution\")\n\n    /** Number trivia multi choice quiz category */\n    object NumberTrivia : MultiChoiceBaseCategory(id = \"number_trivia\")\n}\n\nobject MultiChoiceBaseCategorySerializer : KSerializer<MultiChoiceBaseCategory> {\n    override fun serialize(encoder: Encoder, value: MultiChoiceBaseCategory) {\n        encoder.encodeString(value.toString())\n    }\n\n    override fun deserialize(decoder: Decoder): MultiChoiceBaseCategory {\n        return MultiChoiceBaseCategory.fromId(decoder.decodeString())\n    }\n\n    override val descriptor: SerialDescriptor\n        get() = PrimitiveSerialDescriptor(\"Category\", PrimitiveKind.STRING)\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/MultiChoiceCategory.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.UiText\n\n@Keep\ndata class MultiChoiceCategory(\n    override val id: String,\n    override val name: UiText,\n    override val image: Any,\n    override val requireInternetConnection: Boolean = true\n) : BaseCategory {\n    override val gameMode: GameMode = GameMode.MULTI_CHOICE\n}\n\nfun MultiChoiceCategory.toBaseCategory() = MultiChoiceBaseCategory.fromId(id)\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/MultiChoiceQuestion.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.model.util.serializers.URISerializer\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport java.net.URI\nimport kotlin.random.Random\n\n@Keep\n@Serializable\ndata class MultiChoiceQuestion(\n    val id: Int,\n    val description: String,\n    @Serializable(with = URISerializer::class)\n    val image: URI? = null,\n    val answers: List<String>,\n    val lang: QuestionLanguage,\n    val category: MultiChoiceBaseCategory,\n    val correctAns: Int,\n    val type: MultiChoiceQuestionType,\n    val difficulty: QuestionDifficulty\n) : java.io.Serializable {\n    constructor(\n        description: String,\n        image: URI? = null,\n        answers: List<String>,\n        lang: QuestionLanguage,\n        category: MultiChoiceBaseCategory,\n        correctAns: Int,\n        type: MultiChoiceQuestionType,\n        difficulty: QuestionDifficulty\n    ) : this(\n        // Generate an id based in all the fields of the question.\n        // Answers are sorted to avoid different ids for the same question with different order of answers.\n        // Correct answer is not included because it depends on the order of the answers.\n        id = (description + image?.toString() + answers.sorted() + lang + category + type + difficulty).hashCode(),\n        description = description,\n        image = image,\n        answers = answers,\n        lang = lang,\n        category = category,\n        correctAns = correctAns,\n        type = type,\n        difficulty = difficulty\n    )\n\n    fun toQuestionStep() = MultiChoiceQuestionStep.NotCurrent(this)\n\n    override fun toString(): String = Json.encodeToString(this)\n\n    override fun hashCode(): Int = id\n\n    override fun equals(other: Any?): Boolean {\n        if (other !is MultiChoiceQuestion) return false\n        return id == other.id\n    }\n}\n\n/**\n * Generates a random [MultiChoiceQuestion].\n *\n * @param id The id of the question.\n * @param correctAns The index of the correct answer.\n */\nfun getBasicMultiChoiceQuestion(\n    id: Int = Random.nextInt(),\n    answers: List<String> = listOf(\n        \"Answer 1\",\n        \"Answer 2\",\n        \"Answer 3\",\n        \"Answer 4\"\n    ),\n    correctAns: Int = (0..answers.lastIndex).random(),\n    difficulty: QuestionDifficulty = QuestionDifficulty.random()\n): MultiChoiceQuestion {\n    return MultiChoiceQuestion(\n        id = id,\n        description = \"Question description\",\n        image = null,\n        answers = answers,\n        lang = QuestionLanguage.EN,\n        category = MultiChoiceBaseCategory.Random,\n        correctAns = correctAns,\n        type = MultiChoiceQuestionType.MULTIPLE,\n        difficulty = difficulty\n    )\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/MultiChoiceQuestionStep.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz\n\nimport androidx.annotation.Keep\nimport kotlinx.serialization.Serializable\n\n@Keep\n@Serializable\nsealed class MultiChoiceQuestionStep : java.io.Serializable {\n    abstract val question: MultiChoiceQuestion\n\n    @Keep\n    @Serializable\n    data class NotCurrent(\n        override val question: MultiChoiceQuestion\n    ) : MultiChoiceQuestionStep(), java.io.Serializable {\n        fun changeToCurrent() = Current(question)\n    }\n\n    @Keep\n    @Serializable\n    data class Current(\n        override val question: MultiChoiceQuestion\n    ) : MultiChoiceQuestionStep(), java.io.Serializable {\n        fun changeToCompleted(\n            correct: Boolean,\n            selectedAnswer: SelectedAnswer,\n            questionTime: Long,\n            skipped: Boolean\n        ) = Completed(question, correct, selectedAnswer, questionTime, skipped)\n    }\n\n    @Keep\n    @Serializable\n    data class Completed(\n        override val question: MultiChoiceQuestion,\n        val correct: Boolean,\n        val selectedAnswer: SelectedAnswer = SelectedAnswer.NONE,\n        val questionTime: Long = 0,\n        val skipped: Boolean = false\n    ) : MultiChoiceQuestionStep(), java.io.Serializable\n\n    fun asCurrent() = Current(question)\n}\n\nfun List<MultiChoiceQuestionStep>.isAllCompleted(): Boolean = all { it is MultiChoiceQuestionStep.Completed }\n\nfun List<MultiChoiceQuestionStep.Completed>.isAllCorrect(): Boolean = all { it.correct }\n\nfun List<MultiChoiceQuestionStep.Completed>.countCorrectQuestions(): Int = count { it.correct }\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/MultiChoiceQuestionType.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz\n\nenum class MultiChoiceQuestionType { MULTIPLE, BOOLEAN }\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/QuestionLanguage.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz\n\nenum class QuestionLanguage { EN }\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/SelectedAnswer.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz\n\nimport kotlinx.serialization.Serializable\n\n@JvmInline\n@Serializable\nvalue class SelectedAnswer private constructor(val index: Int) : java.io.Serializable {\n    companion object {\n        val NONE = SelectedAnswer(-1)\n\n        fun fromIndex(index: Int): SelectedAnswer = SelectedAnswer(index)\n    }\n\n    private val isNone: Boolean\n        get() = index == -1\n\n    val isSelected: Boolean\n        get() = !isNone\n\n    infix fun isCorrect(question: MultiChoiceQuestion): Boolean =\n        !isNone && question.correctAns == index\n\n    init {\n        require(index >= -1) { \"SelectedAnswer index must be greater than -1\" }\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/logo_quiz/LogoQuizBaseItem.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz.logo_quiz\n\nimport androidx.annotation.Keep\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\n\n@Keep\n@Serializable\ndata class LogoQuizBaseItem(\n    val description: String,\n    val name: String,\n    @SerialName(\"img_url\") val imgUrl: String,\n    @SerialName(\"incorrect_answers\") val incorrectAnswers: List<String>,\n    val difficulty: String\n)\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/multi_choice_quiz/saved/SortSavedQuestionsBy.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz.saved\n\nenum class SortSavedQuestionsBy {\n    BY_DEFAULT,\n    BY_DESCRIPTION,\n    BY_CATEGORY\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/number/NumberTriviaQuestion.kt",
    "content": "package com.infinitepower.newquiz.model.number\n\nimport androidx.annotation.Keep\nimport kotlinx.serialization.Serializable\n\n@Keep\n@Serializable\ndata class NumberTriviaQuestion(\n    val number: Int,\n    val question: String\n) : java.io.Serializable\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/number/NumberTriviaQuestionsEntity.kt",
    "content": "package com.infinitepower.newquiz.model.number\n\nimport androidx.annotation.Keep\nimport kotlinx.serialization.Serializable\n\n/**\n * Entity for NumberTriviaQuestions\n *\n * @property message error message\n */\n@Keep\n@Serializable\ndata class NumberTriviaQuestionsEntity(\n    val questions: List<NumberTriviaQuestionEntity>,\n    val message: String? = null\n) : java.io.Serializable {\n    fun toNumberTriviaQuestions(): List<NumberTriviaQuestion> {\n        if (message != null) error(message)\n\n        return questions.map { entity ->\n            NumberTriviaQuestion(\n                number = entity.number,\n                question = entity.question\n            )\n        }\n    }\n}\n\n@Keep\n@Serializable\ndata class NumberTriviaQuestionEntity(\n    val number: Int,\n    val question: String\n) : java.io.Serializable\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/question/QuestionDifficulty.kt",
    "content": "package com.infinitepower.newquiz.model.question\n\nimport kotlinx.serialization.Serializable\nimport kotlin.random.Random\n\n@Serializable\nsealed class QuestionDifficulty(\n    val id: String\n) : java.io.Serializable {\n    companion object {\n        fun items() = listOf(Easy, Medium, Hard)\n\n        fun random(\n            random: Random = Random\n        ) = items().random(random)\n\n        fun from(id: String): QuestionDifficulty = when (id) {\n            Easy.id -> Easy\n            Medium.id -> Medium\n            Hard.id -> Hard\n            else -> throw IllegalArgumentException(\"Question difficulty not found\")\n        }\n    }\n\n    override fun toString(): String = id\n\n    @Serializable\n    object Easy : QuestionDifficulty(id = \"easy\")\n\n    @Serializable\n    object Medium : QuestionDifficulty(id = \"medium\")\n\n    @Serializable\n    object Hard : QuestionDifficulty(id = \"hard\")\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/regional_preferences/DistanceUnitType.kt",
    "content": "package com.infinitepower.newquiz.model.regional_preferences\n\nenum class DistanceUnitType {\n    METRIC,\n    IMPERIAL,\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/regional_preferences/RegionalPreferences.kt",
    "content": "package com.infinitepower.newquiz.model.regional_preferences\n\nimport java.util.Locale\n\n/** *\n * @param locale The locale of the user.\n */\ndata class RegionalPreferences(\n    val locale: Locale = Locale.getDefault(),\n    val temperatureUnit: TemperatureUnit? = null,\n    val distanceUnitType: DistanceUnitType? = null,\n)\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/regional_preferences/TemperatureUnit.kt",
    "content": "package com.infinitepower.newquiz.model.regional_preferences\n\nenum class TemperatureUnit(\n    val key: String,\n    val value: String,\n) {\n    CELSIUS(key = \"celsius\", value = \"°C\"),\n    FAHRENHEIT(key = \"fahrenhe\", value = \"°F\"),\n    KELVIN(key = \"kelvin\", value = \"K\");\n\n    companion object {\n        fun fromKey(key: String): TemperatureUnit = entries\n            .firstOrNull { it.key == key }\n            ?: throw IllegalArgumentException(\"Unknown temperature unit: $key\")\n\n    }\n\n    @Suppress(\"MagicNumber\")\n    fun convert(\n        to: TemperatureUnit,\n        value: Double\n    ): Double {\n        if (this == to) return value\n\n        return when (this) {\n            CELSIUS -> when (to) {\n                FAHRENHEIT -> (value * 9 / 5) + 32\n                KELVIN -> value + 273.15\n                else -> value\n            }\n\n            FAHRENHEIT -> when (to) {\n                CELSIUS -> (value - 32) * 5 / 9\n                KELVIN -> (value + 459.67) * 5 / 9\n                else -> value\n            }\n\n            KELVIN -> when (to) {\n                CELSIUS -> value - 273.15\n                FAHRENHEIT -> (value * 9 / 5) - 459.67\n                else -> value\n            }\n        }\n    }\n\n    override fun toString(): String = key\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/util/base64/Base64.kt",
    "content": "package com.infinitepower.newquiz.model.util.base64\n\n/**\n * Encode a [String] to Base64 standard encoded [String].\n */\nval String.base64Encoded: String\n    get() = encodeInternal(Base64Encoding.Standard)\n\n/**\n * Encode a [ByteArray] to Base64 standard encoded [String].\n */\nval ByteArray.base64Encoded: String\n    get() = asCharArray().concatToString().base64Encoded\n\n/**\n * Decode a Base64 standard encoded [String] to [String].\n */\nval String.base64Decoded: String\n    get() = decodeInternal(Base64Encoding.Standard)\n        .map { it.toChar() }\n        .joinToString(\"\")\n        .dropLast(count { it == '=' })\n\n/**\n * Decode a Base64 standard encoded [String] to [ByteArray].\n */\nval String.base64DecodedBytes: ByteArray\n    get() = decodeInternal(Base64Encoding.Standard)\n        .map { it.toByte() }\n        .toList()\n        .dropLast(count { it == '=' })\n        .toByteArray()\n\n/**\n * Decode a Base64 standard encoded [ByteArray] to [String].\n */\nval ByteArray.base64Decoded: String\n    get() = asCharArray().concatToString().base64Decoded\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/util/base64/Base64Encoding.kt",
    "content": "@file:Suppress(\"MagicNumber\")\n\npackage com.infinitepower.newquiz.model.util.base64\n\ninternal sealed interface Base64Encoding {\n    val alphabet: String\n    val requiresPadding: Boolean\n\n    object Standard : Base64Encoding {\n        override val alphabet: String = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n        override val requiresPadding: Boolean = true\n    }\n\n    object UrlSafe : Base64Encoding {\n        override val alphabet: String = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\"\n        override val requiresPadding: Boolean = false // Padding is optional\n    }\n}\n\ninternal fun String.encodeInternal(encoding: Base64Encoding): String {\n    val padLength = when (length % 3) {\n        1 -> 2\n        2 -> 1\n        else -> 0\n    }\n\n    val raw = this + 0.toChar().toString().repeat(maxOf(0, padLength))\n\n    val encoded = raw.chunkedSequence(3) {\n        Triple(it[0].code, it[1].code, it[2].code)\n    }.map { (first, second, third) ->\n        (0xFF.and(first) shl 16) + (0xFF.and(second) shl 8) + 0xFF.and(third)\n    }.map { n ->\n        sequenceOf((n shr 18) and 0x3F, (n shr 12) and 0x3F, (n shr 6) and 0x3F, n and 0x3F)\n    }.flatten()\n        .map { encoding.alphabet[it] }\n        .joinToString(\"\")\n        .dropLast(padLength)\n\n    return when (encoding.requiresPadding) {\n        true -> encoded.padEnd(encoded.length + padLength, '=')\n        else -> encoded\n    }\n}\n\ninternal fun String.decodeInternal(encoding: Base64Encoding): Sequence<Int> {\n    val padLength = when (length % 4) {\n        1 -> 3\n        2 -> 2\n        3 -> 1\n        else -> 0\n    }\n\n    return padEnd(length + padLength, '=')\n        .replace(\"=\", \"A\")\n        .chunkedSequence(4) {\n            (encoding.alphabet.indexOf(it[0]) shl 18) + (encoding.alphabet.indexOf(it[1]) shl 12) +\n                    (encoding.alphabet.indexOf(it[2]) shl 6) + encoding.alphabet.indexOf(it[3])\n        }\n        .map { sequenceOf(0xFF.and(it shr 16), 0xFF.and(it shr 8), 0xFF.and(it)) }\n        .flatten()\n}\n\ninternal fun ByteArray.asCharArray(): CharArray {\n    val chars = CharArray(size)\n    for (i in chars.indices) {\n        chars[i] = get(i).toInt().toChar()\n    }\n    return chars\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/util/base64/Base64Url.kt",
    "content": "package com.infinitepower.newquiz.model.util.base64\n\n/**\n * Encode a [String] to Base64 URL-safe encoded [String].\n */\nval String.base64UrlEncoded: String\n    get() = encodeInternal(Base64Encoding.UrlSafe)\n\n/**\n * Encode a [ByteArray] to Base64 URL-safe encoded [String].\n */\nval ByteArray.base64UrlEncoded: String\n    get() = asCharArray().concatToString().base64UrlEncoded\n\n/**\n * Decode a Base64 URL-safe encoded [String] to [String].\n */\nval String.base64UrlDecoded: String\n    get() {\n        val ret = decodeInternal(Base64Encoding.UrlSafe).map { it.toChar() }\n        val foo = ret.joinToString(\"\")\n        val bar = foo.dropLast(count { it == '=' })\n        return bar.filterNot { it.code == 0 }\n    }\n\n/**\n * Decode a Base64 URL-safe encoded [String] to [ByteArray].\n */\nval String.base64UrlDecodedBytes: ByteArray\n    get() = decodeInternal(Base64Encoding.UrlSafe)\n        .map { it.toByte() }\n        .toList()\n        .dropLast(count { it == '=' })\n        .toByteArray()\n\n/**\n * Decode a Base64 URL-safe encoded [ByteArray] to [String].\n */\nval ByteArray.base64UrlDecoded: String\n    get() = asCharArray().concatToString().base64UrlDecoded\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/util/serializers/URISerializer.kt",
    "content": "package com.infinitepower.newquiz.model.util.serializers\n\nimport kotlinx.serialization.KSerializer\nimport kotlinx.serialization.descriptors.PrimitiveKind\nimport kotlinx.serialization.descriptors.PrimitiveSerialDescriptor\nimport kotlinx.serialization.descriptors.SerialDescriptor\nimport kotlinx.serialization.encoding.Decoder\nimport kotlinx.serialization.encoding.Encoder\nimport java.net.URI\n\nobject URISerializer : KSerializer<URI> {\n    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(\n        serialName = \"URI\",\n        kind = PrimitiveKind.STRING\n    )\n\n    override fun serialize(encoder: Encoder, value: URI) {\n        encoder.encodeString(value.toASCIIString())\n    }\n\n    override fun deserialize(decoder: Decoder): URI {\n        return URI.create(decoder.decodeString())\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/wordle/WordleCategory.kt",
    "content": "package com.infinitepower.newquiz.model.wordle\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.BaseCategory\nimport com.infinitepower.newquiz.model.GameMode\nimport com.infinitepower.newquiz.model.UiText\n\n@Keep\ndata class WordleCategory(\n    val wordleQuizType: WordleQuizType,\n    override val id: String = wordleQuizType.name,\n    override val name: UiText,\n    override val image: Any,\n    override val requireInternetConnection: Boolean = false\n) : BaseCategory {\n    override val gameMode: GameMode = GameMode.WORDLE\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/wordle/WordleItem.kt",
    "content": "package com.infinitepower.newquiz.model.wordle\n\n@JvmInline\nvalue class WordleChar(val value: Char) {\n    companion object {\n        val Empty = WordleChar(' ')\n    }\n\n    override fun toString(): String = value.toString()\n\n    fun isEmpty(): Boolean = value == ' '\n}\n\nsealed class WordleItem {\n    abstract val char: WordleChar\n\n    object Empty : WordleItem() {\n        override val char: WordleChar\n            get() = WordleChar.Empty\n    }\n\n    data class None(\n        override val char: WordleChar,\n        val verified: Boolean = false\n    ) : WordleItem()\n\n    data class Present(\n        override val char: WordleChar\n    ) : WordleItem()\n\n    data class Correct(\n        override val char: WordleChar\n    ) : WordleItem()\n\n    val isCompleted: Boolean\n        get() = this !is Empty\n\n    val isVerified: Boolean\n        get() = when (this) {\n            is Empty -> false\n            is None -> this.verified\n            else -> true\n        }\n\n    companion object {\n        /** Creates [None] wordle item from char with false verified */\n        fun fromChar(char: Char) = None(WordleChar(char), false)\n    }\n}\n\ninline fun <reified T> List<WordleItem>.countByItem(): Int = filterIsInstance<T>().count()\n\nfun List<WordleItem>.itemsToString(): String = joinToString(\"\") { item ->\n    item.char.toString()\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/wordle/WordleQuizType.kt",
    "content": "package com.infinitepower.newquiz.model.wordle\n\nenum class WordleQuizType {\n    TEXT,\n    NUMBER,\n    MATH_FORMULA,\n    NUMBER_TRIVIA\n}\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/wordle/WordleRowItem.kt",
    "content": "package com.infinitepower.newquiz.model.wordle\n\n@JvmInline\nvalue class WordleRowItem(\n    val items: List<WordleItem>\n) {\n    val isRowCorrect: Boolean\n        get() = items.all { item -> item is WordleItem.Correct }\n\n    val isRowCompleted: Boolean\n        get() = items.all { item -> item.isCompleted }\n\n    val isRowVerified: Boolean\n        get() = items.all { item -> item.isVerified }\n}\n\nfun emptyRowItem(size: Int = 6): WordleRowItem = WordleRowItem(\n    items = List(size) {\n        WordleItem.Empty\n    }\n)\n"
  },
  {
    "path": "model/src/main/java/com/infinitepower/newquiz/model/wordle/WordleWord.kt",
    "content": "package com.infinitepower.newquiz.model.wordle\n\nimport androidx.annotation.Keep\n\n@Keep\ndata class WordleWord(\n    val word: String,\n    val textHelper: String? = null\n)\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/RemainingTimeTest.kt",
    "content": "package com.infinitepower.newquiz.model\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.minutes\nimport kotlin.time.Duration.Companion.seconds\n\ninternal class RemainingTimeTest {\n    @Test\n    fun `creates a new instance of RemainingTime from zero time`() {\n        val remainingTime = RemainingTime.ZERO\n\n        assertThat(remainingTime.value).isEqualTo(Duration.ZERO)\n    }\n\n    @Test\n    fun `creates a new instance of RemainingTime from the given duration value`() {\n        val duration = 5.milliseconds\n        val remainingTime = RemainingTime(duration)\n\n        assertThat(remainingTime.value).isEqualTo(duration)\n\n        val duration2 = 12.seconds\n        val remainingTime2 = RemainingTime(duration2)\n\n        assertThat(remainingTime2.value).isEqualTo(duration2)\n    }\n\n    @Test\n    fun `fromMilliseconds creates a new instance of RemainingTime from the given milliseconds value`() {\n        val millis = 300000L\n        val remainingTime = RemainingTime.fromMilliseconds(millis)\n\n        assertThat(remainingTime.value).isEqualTo(millis.milliseconds)\n    }\n\n    @Test\n    fun `fromMilliseconds throws an exception when the value is lower than 0`() {\n        val negativeMillis = -1000L\n\n        val exception = assertThrows<IllegalArgumentException> {\n            RemainingTime.fromMilliseconds(negativeMillis)\n        }\n\n        assertThat(exception)\n            .hasMessageThat()\n            .isEqualTo(\"RemainingTime value must be greater than or equal to 0\")\n    }\n\n    @Test\n    fun `getRemainingPercent returns the remaining percentage of the time`() {\n        val duration = 10.seconds\n        val maxTime = 50.seconds\n        val remainingTime = RemainingTime(duration)\n\n        assertThat(remainingTime.getRemainingPercent(maxTime)).isEqualTo(0.2)\n    }\n\n    @Test\n    fun `minuteSecondFormatted returns the remaining time in minute second format`() {\n        val duration = 5.minutes + 10.seconds\n        val remainingTime = RemainingTime(duration)\n\n        assertThat(remainingTime.toMinuteSecondFormatted()).isEqualTo(\"5:10\")\n    }\n\n    @Test\n    fun `getElapsedSeconds returns the elapsed seconds from the max time`() {\n        val duration = 10.seconds\n        val maxTime = 60.seconds\n        val remainingTime = RemainingTime(duration)\n\n        val result = remainingTime.getElapsedSeconds(maxTime)\n        assertThat(result).isEqualTo(50)\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/category/ShowCategoryConnectionInfoTest.kt",
    "content": "package com.infinitepower.newquiz.model.category\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.CsvSource\n\n/**\n * Tests for [ShowCategoryConnectionInfo]\n */\ninternal class ShowCategoryConnectionInfoTest {\n    @ParameterizedTest\n    @CsvSource(\n        \"NONE, false, false\",\n        \"NONE, true, false\",\n        \"BOTH, false, true\",\n        \"BOTH, true, true\",\n        \"REQUIRE_CONNECTION, false, false\",\n        \"REQUIRE_CONNECTION, true, true\",\n        \"DONT_REQUIRE_CONNECTION, false, true\",\n        \"DONT_REQUIRE_CONNECTION, true, false\",\n    )\n    fun test_shouldShowBadge(\n        showCategoryConnectionInfo: ShowCategoryConnectionInfo,\n        requireInternetConnection: Boolean,\n        expected: Boolean,\n    ) {\n        assertThat(showCategoryConnectionInfo.shouldShowBadge(requireInternetConnection)).isEqualTo(expected)\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonQuizCategoryTest.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.NumberFormatType\nimport com.infinitepower.newquiz.model.toUiText\nimport org.junit.jupiter.api.Test\n\ninternal class ComparisonQuizCategoryTest {\n    @Test\n    fun `getQuestionDescription returns correct string for GREATER comparison mode`() {\n        val questionDescription = ComparisonQuizCategory.QuestionDescription(\n            greater = \"Which is greater?\",\n            less = \"Which is less?\"\n        )\n\n        val category = ComparisonQuizCategory(\n            id = \"1\",\n            name = \"Category Title\".toUiText(),\n            description = \"Category Description\",\n            image = \"https://example.com/image.png\",\n            questionDescription = questionDescription,\n            formatType = NumberFormatType.DEFAULT,\n            helperValueSuffix = null,\n            dataSourceAttribution = null\n        )\n\n        val description = category.getQuestionDescription(ComparisonMode.GREATER)\n        assertThat(description).isEqualTo(\"Which is greater?\")\n    }\n\n    @Test\n    fun `getQuestionDescription returns correct string for LESSER comparison mode`() {\n        val questionDescription = ComparisonQuizCategory.QuestionDescription(\n            greater = \"Which is greater?\",\n            less = \"Which is less?\"\n        )\n\n        val category = ComparisonQuizCategory(\n            id = \"1\",\n            name = \"Category Title\".toUiText(),\n            description = \"Category Description\",\n            image = \"https://example.com/image.png\",\n            questionDescription = questionDescription,\n            formatType = NumberFormatType.DEFAULT,\n            helperValueSuffix = null,\n            dataSourceAttribution = null\n        )\n\n        val description = category.getQuestionDescription(ComparisonMode.LESSER)\n        assertThat(description).isEqualTo(\"Which is less?\")\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/comparison_quiz/ComparisonQuizQuestionTest.kt",
    "content": "package com.infinitepower.newquiz.model.comparison_quiz\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.api.Test\nimport java.net.URI\n\ninternal class ComparisonQuizQuestionTest {\n    private val emptyUri = URI(\"\")\n\n    @Test\n    fun `nextQuestion returns a new ComparisonQuizCurrentQuestion with the second question replaced`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = emptyUri,\n            value = 5.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = emptyUri,\n            value = 10.0\n        )\n\n        val question = ComparisonQuizQuestion(\n            questions = quizItem1 to quizItem2,\n            categoryId = \"\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        val newQuestion = ComparisonQuizItem(\n            title = \"C\",\n            imgUri = emptyUri,\n            value = 7.0\n        )\n\n        assertThat(question.questions.first).isEqualTo(quizItem1)\n        assertThat(question.questions.second).isEqualTo(quizItem2)\n\n        val updatedQuestion = question.nextQuestion(newQuestion)\n\n        assertThat(updatedQuestion.questions.first).isEqualTo(quizItem2)\n        assertThat(updatedQuestion.questions.second).isEqualTo(newQuestion)\n    }\n\n    @Test\n    fun `test isCorrectAnswer when correct answer is first and the user answer is first`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = emptyUri,\n            value = 2.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = emptyUri,\n            value = 1.0\n        )\n\n        val question = ComparisonQuizQuestion(\n            questions = quizItem1 to quizItem2,\n            categoryId = \"\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        val isCorrectGreater = question.isCorrectAnswer(quizItem1)\n        assertThat(isCorrectGreater).isTrue()\n    }\n\n    @Test\n    fun `test isCorrectAnswer when correct answer is first and the user answer is second`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = emptyUri,\n            value = 2.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = emptyUri,\n            value = 1.0\n        )\n\n        val question = ComparisonQuizQuestion(\n            questions = quizItem1 to quizItem2,\n            categoryId = \"\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        val isCorrectGreater = question.isCorrectAnswer(quizItem2)\n        assertThat(isCorrectGreater).isFalse()\n    }\n\n    @Test\n    fun `test isCorrectAnswer when correct answer is second and the user answer is first`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = emptyUri,\n            value = 1.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = emptyUri,\n            value = 2.0\n        )\n\n        val question = ComparisonQuizQuestion(\n            questions = quizItem1 to quizItem2,\n            categoryId = \"\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        val isCorrectGreater = question.isCorrectAnswer(quizItem1)\n        assertThat(isCorrectGreater).isFalse()\n    }\n\n    @Test\n    fun `test isCorrectAnswer when correct answer is second and the user answer is second`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = emptyUri,\n            value = 1.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = emptyUri,\n            value = 2.0\n        )\n\n        val question = ComparisonQuizQuestion(\n            questions = quizItem1 to quizItem2,\n            categoryId = \"\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        val isCorrectGreater = question.isCorrectAnswer(quizItem2)\n        assertThat(isCorrectGreater).isTrue()\n    }\n\n    @Test\n    fun `test isCorrectAnswer when values are the same`() {\n        val quizItem1 = ComparisonQuizItem(\n            title = \"A\",\n            imgUri = emptyUri,\n            value = 1.0\n        )\n\n        val quizItem2 = ComparisonQuizItem(\n            title = \"B\",\n            imgUri = emptyUri,\n            value = 1.0\n        )\n\n        val question = ComparisonQuizQuestion(\n            questions = quizItem1 to quizItem2,\n            categoryId = \"\",\n            comparisonMode = ComparisonMode.GREATER\n        )\n\n        val isCorrectGreater = question.isCorrectAnswer(quizItem1)\n        assertThat(isCorrectGreater).isTrue()\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/daily_challenge/DailyChallengeTaskTest.kt",
    "content": "package com.infinitepower.newquiz.model.daily_challenge\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport com.infinitepower.newquiz.model.toUiText\nimport kotlinx.datetime.Clock\nimport kotlinx.datetime.TimeZone\nimport kotlinx.datetime.toLocalDateTime\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport kotlin.time.Duration.Companion.days\n\n/**\n * Tests for [DailyChallengeTask].\n */\ninternal class DailyChallengeTaskTest {\n    @Test\n    fun `test daily challenge task isDaily, when date range is one day long, returns true`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = now.rangeTo(now + 1.days), // Not expired, one day long\n            currentValue = 0u, // Not completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isDaily()).isTrue()\n    }\n\n    @Test\n    fun `test daily challenge task isDaily, when date range is not one day long, returns false`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = now.rangeTo(now + 5.days), // Not expired, one day long\n            currentValue = 0u, // Not completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isDaily()).isFalse()\n    }\n\n    @Test\n    fun `test when the current time is not in the task's date range`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = (now - 2.days).rangeTo(now - 1.days), // Expired\n            currentValue = 0u, // Not completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isExpired()).isTrue()\n        assertThat(task.isCompleted()).isFalse()\n\n        // The task is expired and not completed, so it is not active.\n        assertThat(task.isActive()).isFalse()\n\n        // The task is not completed, so it is not claimable.\n        assertThat(task.isClaimable()).isFalse()\n    }\n\n    @Test\n    fun `test when the current time is in the task's date range`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = (now - 1.days).rangeTo(now + 1.days), // Not expired\n            currentValue = 0u, // Not completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isExpired()).isFalse()\n        assertThat(task.isCompleted()).isFalse()\n\n        // The task is not expired and not completed, so it is active.\n        assertThat(task.isActive()).isTrue()\n\n        // The task is not completed, so it is not claimable.\n        assertThat(task.isClaimable()).isFalse()\n    }\n\n    @Test\n    fun `test when the current value is equal to the maximum value`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = (now - 1.days).rangeTo(now + 1.days), // Not expired\n            currentValue = 1u, // Completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isExpired()).isFalse()\n        assertThat(task.isCompleted()).isTrue()\n\n        // The task is not expired and not claimed, so it is active.\n        assertThat(task.isActive()).isTrue()\n\n        // The task is completed, so it is claimable.\n        assertThat(task.isClaimable()).isTrue()\n    }\n\n    @Test\n    fun `test when the task is already claimed`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = true,\n            dateRange = (now - 1.days).rangeTo(now + 1.days), // Not expired\n            currentValue = 1u, // Completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isExpired()).isFalse()\n        assertThat(task.isCompleted()).isTrue()\n        assertThat(task.isActive()).isFalse()\n        assertThat(task.isClaimable()).isFalse()\n    }\n\n    @Test\n    fun `test when the task is expired`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = (now - 2.days).rangeTo(now - 1.days), // Expired\n            currentValue = 1u, // Completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isExpired()).isTrue()\n        assertThat(task.isCompleted()).isTrue()\n\n        // The task is expired and completed, so it is not active.\n        assertThat(task.isActive()).isFalse()\n\n        // The task is completed, so it is claimable.\n        assertThat(task.isClaimable()).isFalse()\n    }\n\n    @Test\n    fun `test when the task is expired and already claimed`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = true,\n            dateRange = (now - 2.days).rangeTo(now - 1.days), // Expired\n            currentValue = 1u, // Completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isExpired()).isTrue()\n        assertThat(task.isCompleted()).isTrue()\n\n        // The task is expired and completed, so it is not active.\n        assertThat(task.isActive()).isFalse()\n\n        // The task is completed, so it is claimable.\n        assertThat(task.isClaimable()).isFalse()\n    }\n\n    @Test\n    fun `test when the task is expired and not completed and already claimed`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = true,\n            dateRange = (now - 2.days).rangeTo(now - 1.days), // Expired\n            currentValue = 0u, // Not completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isExpired()).isTrue()\n        assertThat(task.isCompleted()).isFalse()\n\n        // The task is expired and not completed, so it is not active.\n        assertThat(task.isActive()).isFalse()\n\n        // The task is not completed, so it is not claimable.\n        assertThat(task.isClaimable()).isFalse()\n    }\n\n    // Test value out of range and date range out of range\n    @Test\n    fun `when current value is greater than the maximum value, should be completed`() {\n        val now = Clock.System.now()\n\n        val task = DailyChallengeTask(\n            id = 1,\n            diamondsReward = 1u,\n            experienceReward = 1u,\n            isClaimed = false,\n            dateRange = (now - 1.days).rangeTo(now + 1.days), // Not expired\n            currentValue = 2u, // Completed\n            maxValue = 1u,\n            event = GameEvent.MultiChoice.PlayQuestions,\n            title = \"Test\".toUiText()\n        )\n\n        assertThat(task.isExpired()).isFalse()\n        assertThat(task.isCompleted()).isTrue()\n    }\n\n    @Test\n    fun `when the date range is invalid, should throw IllegalArgumentException`() {\n        val now = Clock.System.now()\n\n        val dateRange = (now + 1.days).rangeTo(now - 1.days)\n\n        val tz = TimeZone.currentSystemDefault()\n        val start = dateRange.start.toLocalDateTime(tz).date\n        val end = dateRange.endInclusive.toLocalDateTime(tz).date\n\n        val e = assertThrows<IllegalArgumentException> {\n            DailyChallengeTask(\n                id = 1,\n                diamondsReward = 1u,\n                experienceReward = 1u,\n                isClaimed = false,\n                dateRange = dateRange, // Not expired\n                currentValue = 1u, // Completed\n                maxValue = 1u,\n                event = GameEvent.MultiChoice.PlayQuestions,\n                title = \"Test\".toUiText()\n            )\n        }\n\n        assertThat(e)\n            .hasMessageThat()\n            .isEqualTo(\"The start date ($start) must be less than or equal to the end date ($end).\")\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/daily_challenge/DailyChallengeTaskTypeTest.kt",
    "content": "package com.infinitepower.newquiz.model.daily_challenge\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\n\n/**\n * Tests for [GameEvent].\n */\ninternal class DailyChallengeTaskTypeTest {\n    @Test\n    fun `test fromKey, when key is not valid, throws IllegalArgumentException`() {\n        assertThrows<IllegalArgumentException> {\n            GameEvent.fromKey(\"invalid\")\n        }\n    }\n\n    @Test\n    fun `test fromKey, when key is valid, returns the correct type`() {\n        val type = GameEvent.fromKey(\"multi_choice_play_questions\")\n\n        assertThat(type).isEqualTo(GameEvent.MultiChoice.PlayQuestions)\n    }\n\n    @Test\n    fun `test fromKey, when key is PlayQuizWithCategory, returns the correct type`() {\n        val keyPrefix = GameEvent.MultiChoice.PlayQuizWithCategory.KEY_PREFIX\n        val categoryKey = \"category_key\"\n\n        val type = GameEvent.fromKey(\"$keyPrefix$categoryKey\")\n        val expectedType = GameEvent.MultiChoice.PlayQuizWithCategory(categoryKey)\n\n        assertThat(type).isEqualTo(expectedType)\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/math_quiz/maze/MazePointTest.kt",
    "content": "package com.infinitepower.newquiz.model.math_quiz.maze\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.maze.MazePoint\nimport com.infinitepower.newquiz.model.maze.generateMazePointsBottomToTop\nimport com.infinitepower.newquiz.model.maze.generateMazePointsTopToBottom\nimport com.infinitepower.newquiz.model.maze.isInsideCircle\nimport org.junit.jupiter.api.Test\n\ninternal class MazePointTest {\n    @Test\n    fun `generate maze points, top to bottom test`() {\n        val points = generateMazePointsTopToBottom(\n            startPoint = MazePoint(50f, 0f),\n            increment = MazePoint(50f, 50f)\n        ).take(8).toList()\n\n        println(points)\n\n        val expectedPoints = listOf(\n            MazePoint(x = 50f, 0f),\n            MazePoint(x = 50f, 50f),\n            MazePoint(x = 100f, 50f),\n            MazePoint(x = 100f, 100f),\n            MazePoint(x = 50f, 100f),\n            MazePoint(x = 0f, 100f),\n            MazePoint(x = 0f, 150f),\n            MazePoint(x = 50f, 150f)\n        )\n\n        assertThat(points).isEqualTo(expectedPoints)\n    }\n\n    @Test\n    fun `generate maze points, bottom to top test`() {\n        val points = generateMazePointsBottomToTop(\n            startPoint = MazePoint(50f, 1000f),\n            increment = MazePoint(50f, 50f)\n        ).take(9).toList()\n\n        println(points)\n\n        val expectedPoints = listOf(\n            MazePoint(x = 50f, 1000f),\n            MazePoint(x = 50f, 950f),\n            MazePoint(x = 100f, 950f),\n            MazePoint(x = 100f, 900f),\n            MazePoint(x = 50f, 900f),\n            MazePoint(x = 0f, 900f),\n            MazePoint(x = 0f, 850f),\n            MazePoint(x = 50f, 850f),\n            MazePoint(x = 100f, 850f)\n        )\n\n        assertThat(points).isEqualTo(expectedPoints)\n    }\n\n    @Test\n    fun `is inside circle test`() {\n        val circlePoint = MazePoint(0f, 0f)\n\n        val testPointInside = MazePoint(1f, 1f)\n        val testPointOutside = MazePoint(2f, 2f)\n\n        // Set the radius of the circle\n        val radius = 2f\n\n        assertThat(testPointInside.isInsideCircle(circlePoint, radius)).isTrue()\n        assertThat(testPointOutside.isInsideCircle(circlePoint, radius)).isFalse()\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/math_quiz/maze/MazeQuizTest.kt",
    "content": "package com.infinitepower.newquiz.model.math_quiz.maze\n\ninternal class MazeQuizTest {\n    /*\n    @Test\n    fun testIsPlayableItem_noPlayedItems() {\n        // Create a list of MazeItem objects\n        val items = listOf(\n            MazeQuiz.MazeItem(formula = MathFormula.fromStringFullFormula(\"1+1=2\")),  // not played\n            MazeQuiz.MazeItem(formula = MathFormula.fromStringFullFormula(\"1+1=2\")), // not played\n            MazeQuiz.MazeItem(formula = MathFormula.fromStringFullFormula(\"1+1=2\")),  // not played\n        )\n\n        // Check if the first item is playable\n        assertThat(items.isPlayableItem(0)).isTrue()\n\n        // Check if the second item is playable\n        assertThat(items.isPlayableItem(1)).isFalse()\n\n        // Check if the third item is playable\n        assertThat(items.isPlayableItem(2)).isFalse()\n    }\n\n    @Test\n    fun testIsPlayableItem_firstPlayed() {\n        // Create a list of MazeItem objects\n        val items = listOf(\n            MazeQuiz.MazeItem(\n                formula = MathFormula.fromStringFullFormula(\"1+1=2\"),\n                played = true\n            ),  // played\n            MazeQuiz.MazeItem(formula = MathFormula.fromStringFullFormula(\"1+1=2\")), // not played\n            MazeQuiz.MazeItem(formula = MathFormula.fromStringFullFormula(\"1+1=2\")),  // not played\n        )\n\n        // Check if the first item is playable\n        assertThat(items.isPlayableItem(0)).isFalse()\n\n        // Check if the second item is playable\n        assertThat(items.isPlayableItem(1)).isTrue()\n\n        // Check if the third item is playable\n        assertThat(items.isPlayableItem(2)).isFalse()\n    }\n\n    @Test\n    fun testIsItemPlayed_playedItem() {\n        val mazeItems = listOf(\n            MazeQuiz.MazeItem(\n                formula = MathFormula.fromStringFullFormula(\"1+1=2\"),\n                played = true\n            ),\n            MazeQuiz.MazeItem(\n                formula = MathFormula.fromStringFullFormula(\"1+1=2\"),\n                played = true\n            ),\n        )\n        assertThat(mazeItems.isItemPlayed(0)).isTrue()\n    }\n\n    @Test\n    fun testIsItemPlayed_unPlayedItem() {\n        val mazeItems = listOf(\n            MazeQuiz.MazeItem(\n                formula = MathFormula.fromStringFullFormula(\"1+1=2\"),\n                played = true\n            ),\n            MazeQuiz.MazeItem(\n                formula = MathFormula.fromStringFullFormula(\"1+1=2\"),\n                played = false\n            ),\n        )\n        assertThat(mazeItems.isItemPlayed(1)).isFalse()\n    }\n\n    @Test\n    fun testIsItemPlayed_outOfBounds() {\n        val mazeItems = listOf(\n            MazeQuiz.MazeItem(\n                formula = MathFormula.fromStringFullFormula(\"1+1=2\"),\n                played = true\n            ),\n            MazeQuiz.MazeItem(\n                formula = MathFormula.fromStringFullFormula(\"1+1=2\"),\n                played = false\n            ),\n        )\n        assertThat(mazeItems.isItemPlayed(2)).isFalse()\n    }\n\n     */\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/multi_choice_quiz/MultiChoiceQuestionTest.kt",
    "content": "package com.infinitepower.newquiz.model.multi_choice_quiz\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport kotlin.test.Test\n\n/**\n * Tests for [MultiChoiceQuestion].\n */\ninternal class MultiChoiceQuestionTest {\n    @Test\n    fun `test question generated id is based in the fields`() {\n        val question = MultiChoiceQuestion(\n            description = \"description\",\n            answers = listOf(\"a\", \"b\", \"c\"),\n            lang = QuestionLanguage.EN,\n            category = MultiChoiceBaseCategory.Random,\n            correctAns = 0,\n            type = MultiChoiceQuestionType.MULTIPLE,\n            difficulty = QuestionDifficulty.Easy\n        )\n\n        assertThat(question.id).isEqualTo(376535089)\n\n        val questionWithShuffledAnswers = MultiChoiceQuestion(\n            description = \"description\",\n            answers = listOf(\"c\", \"b\", \"a\"),\n            lang = QuestionLanguage.EN,\n            category = MultiChoiceBaseCategory.Random,\n            correctAns = 0,\n            type = MultiChoiceQuestionType.MULTIPLE,\n            difficulty = QuestionDifficulty.Easy\n        )\n\n        assertThat(questionWithShuffledAnswers.id).isEqualTo(376535089)\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/util/base64/Base64Test.kt",
    "content": "package com.infinitepower.newquiz.model.util.base64\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertArrayEquals\nimport org.junit.jupiter.api.Test\n\ninternal class Base64Test {\n    @Test\n    fun byteArray_base64Decoded() {\n        assertEquals(\"Hello, world!\", \"SGVsbG8sIHdvcmxkIQ==\".encodeToByteArray().base64Decoded)\n        assertArrayEquals(\n            byteArrayOf(\n                -94, 124, -26, -112, -72, -84, 16, 11, 67, -45, 107, 38, -99, 79, 62, -49, 83, 26, -85, -70, -122, 53,\n                67, 42, -94, -87, 61, -74, 66, 0, 80, -125, -17, -11, -125, 63, 109, -15, 56, -95, -33, 18, 110, 47,\n                47, -20, -72, -34, 53, -69, 49, -45, 54, 53, -21, 43, 9, -84, -125, 72, -61, 76, 31, -46\n            ),\n            \"onzmkLisEAtD02smnU8+z1Maq7qGNUMqoqk9tkIAUIPv9YM/bfE4od8Sbi8v7LjeNbsx0zY16ysJrINIw0wf0g==\".base64DecodedBytes\n        )\n    }\n\n    @Test\n    fun byteArray_base64Encoded() {\n        assertEquals(\n            \"xvrp9DBWlei2mG0ov9MN+A==\", // value1\n            byteArrayOf(-58, -6, -23, -12, 48, 86, -107, -24, -74, -104, 109, 40, -65, -45, 13, -8).base64Encoded\n        )\n        assertEquals(\n            \"IkYJxF8nIQD9RY7Yk6r26A==\", // value222\n            byteArrayOf(34, 70, 9, -60, 95, 39, 33, 0, -3, 69, -114, -40, -109, -86, -10, -24).base64Encoded\n        )\n        assertEquals(\n            \"U0GeVBi2dNcdL2IO0nJo5Q==\", // value555\n            byteArrayOf(83, 65, -98, 84, 24, -74, 116, -41, 29, 47, 98, 14, -46, 114, 104, -27).base64Encoded\n        )\n    }\n\n    @Test\n    fun string_base64Decoded() {\n        assertEquals(\"word\", \"d29yZA==\".base64Decoded)\n        assertEquals(\"Word\", \"V29yZA==\".base64Decoded)\n        assertEquals(\"Hello\", \"SGVsbG8=\".base64Decoded)\n        assertEquals(\"World!\", \"V29ybGQh\".base64Decoded)\n        assertEquals(\"Hello, world!\", \"SGVsbG8sIHdvcmxkIQ==\".base64Decoded)\n        assertEquals(\n            Base64Encoding.Standard.alphabet,\n            \"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLw==\".base64Decoded\n        )\n        assertEquals(\"abcd\", \"YWJjZA==\".base64Decoded)\n        assertEquals(\n            \"1234567890-=!@#\\$%^&*()_+qwertyuiop[];'\\\\,./?><|\\\":}{P`~\",\n            \"MTIzNDU2Nzg5MC09IUAjJCVeJiooKV8rcXdlcnR5dWlvcFtdOydcLC4vPz48fCI6fXtQYH4=\".base64Decoded\n        )\n        assertEquals(\"saschpe\", \"c2FzY2hwZQ==\".base64Decoded)\n    }\n\n    @Test\n    fun string_base64Encoded() {\n        assertEquals(\"d29yZA==\", \"word\".base64Encoded)\n        assertEquals(\"V29yZA==\", \"Word\".base64Encoded)\n        assertEquals(\"SGVsbG8=\", \"Hello\".base64Encoded)\n        assertEquals(\"V29ybGQh\", \"World!\".base64Encoded)\n        assertEquals(\"SGVsbG8sIHdvcmxkIQ==\", \"Hello, world!\".base64Encoded)\n        assertEquals(\"SGVsbG8sIHdvcmxkIQ==\", \"Hello, world!\".encodeToByteArray().base64Encoded)\n        assertEquals(\n            \"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLw==\",\n            Base64Encoding.Standard.alphabet.base64Encoded\n        )\n        assertEquals(\"YWJjZA==\", \"abcd\".base64Encoded)\n        assertEquals(\n            \"MTIzNDU2Nzg5MC09IUAjJCVeJiooKV8rcXdlcnR5dWlvcFtdOydcLC4vPz48fCI6fXtQYH4=\",\n            \"1234567890-=!@#\\$%^&*()_+qwertyuiop[];'\\\\,./?><|\\\":}{P`~\".base64Encoded\n        )\n        assertEquals(\"c2FzY2hwZQ==\", \"saschpe\".base64Encoded)\n    }\n\n    @Test\n    fun string_roundTrip() {\n        assertEquals(\n            Base64Encoding.Standard.alphabet,\n            Base64Encoding.Standard.alphabet.base64Encoded.base64Decoded\n        )\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/util/base64/Base64UrlTest.kt",
    "content": "package com.infinitepower.newquiz.model.util.base64\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertArrayEquals\nimport org.junit.jupiter.api.Test\n\ninternal class Base64UrlTest {\n    @Test\n    fun byteArray_base64UrlDecoded() {\n        assertEquals(\"Hello, world!\", \"SGVsbG8sIHdvcmxkIQ==\".encodeToByteArray().base64UrlDecoded)\n        assertArrayEquals(\n            byteArrayOf(\n                -94, 124, -26, -112, -72, -84, 16, 11, 67, -45, 107, 38, -99, 79, 62, -49, 83, 26, -85, -70, -122, 53,\n                67, 42, -94, -87, 61, -74, 66, 0, 80, -125, -17, -11, -125, 63, 109, -15, 56, -95, -33, 18, 110, 47,\n                47, -20, -72, -34, 53, -69, 49, -45, 54, 53, -21, 43, 9, -84, -125, 72, -61, 76, 31, -46\n            ),\n            \"onzmkLisEAtD02smnU8-z1Maq7qGNUMqoqk9tkIAUIPv9YM_bfE4od8Sbi8v7LjeNbsx0zY16ysJrINIw0wf0g==\".base64UrlDecodedBytes\n        )\n    }\n\n    @Test\n    fun byteArray_base64UrlEncoded() {\n        assertEquals(\n            \"xvrp9DBWlei2mG0ov9MN-A\", // value1\n            byteArrayOf(-58, -6, -23, -12, 48, 86, -107, -24, -74, -104, 109, 40, -65, -45, 13, -8).base64UrlEncoded\n        )\n        assertEquals(\n            \"IkYJxF8nIQD9RY7Yk6r26A\", // value222\n            byteArrayOf(34, 70, 9, -60, 95, 39, 33, 0, -3, 69, -114, -40, -109, -86, -10, -24).base64UrlEncoded\n        )\n        assertEquals(\n            \"U0GeVBi2dNcdL2IO0nJo5Q\", // value555\n            byteArrayOf(83, 65, -98, 84, 24, -74, 116, -41, 29, 47, 98, 14, -46, 114, 104, -27).base64UrlEncoded\n        )\n    }\n\n    @Test\n    fun string_base64UrlDecoded() {\n        assertEquals(\"word\", \"d29yZA==\".base64UrlDecoded)\n        assertEquals(\"Word\", \"V29yZA==\".base64UrlDecoded)\n        assertEquals(\"Hello\", \"SGVsbG8=\".base64UrlDecoded)\n        assertEquals(\"World!\", \"V29ybGQh\".base64UrlDecoded)\n        assertEquals(\"Hello, world!\", \"SGVsbG8sIHdvcmxkIQ==\".base64UrlDecoded)\n        assertEquals(\n            Base64Encoding.Standard.alphabet,\n            \"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLw==\".base64UrlDecoded\n        )\n        assertEquals(\"Salt\", \"U2FsdA==\".base64UrlDecoded)\n        assertEquals(\"Pepper\", \"UGVwcGVy\".base64UrlDecoded)\n        assertEquals(\"abcd\", \"YWJjZA\".base64UrlDecoded)\n        assertEquals(\n            \"1234567890-=!@#\\$%^&*()_+qwertyuiop[];'\\\\,./?><|\\\":}{P`~\",\n            \"MTIzNDU2Nzg5MC09IUAjJCVeJiooKV8rcXdlcnR5dWlvcFtdOydcLC4vPz48fCI6fXtQYH4\".base64UrlDecoded\n        )\n        assertEquals(\"saschpe\", \"c2FzY2hwZQ\".base64UrlDecoded)\n    }\n\n    @Test\n    fun string_base64UrlEncoded() {\n        assertEquals(\"d29yZA\", \"word\".base64UrlEncoded)\n        assertEquals(\"V29yZA\", \"Word\".base64UrlEncoded)\n        assertEquals(\"SGVsbG8\", \"Hello\".base64UrlEncoded)\n        assertEquals(\"V29ybGQh\", \"World!\".base64UrlEncoded)\n        assertEquals(\"SGVsbG8sIHdvcmxkIQ\", \"Hello, world!\".base64UrlEncoded)\n        assertEquals(\"SGVsbG8sIHdvcmxkIQ\", \"Hello, world!\".encodeToByteArray().base64UrlEncoded)\n        assertEquals(\n            \"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLw\",\n            Base64Encoding.Standard.alphabet.base64UrlEncoded\n        )\n        assertEquals(\"U2FsdA\", \"Salt\".base64UrlEncoded)\n        assertEquals(\"UGVwcGVy\", \"Pepper\".base64UrlEncoded)\n        assertEquals(\"YWJjZA\", \"abcd\".base64UrlEncoded)\n        assertEquals(\n            \"MTIzNDU2Nzg5MC09IUAjJCVeJiooKV8rcXdlcnR5dWlvcFtdOydcLC4vPz48fCI6fXtQYH4\",\n            \"1234567890-=!@#\\$%^&*()_+qwertyuiop[];'\\\\,./?><|\\\":}{P`~\".base64UrlEncoded\n        )\n        assertEquals(\"c2FzY2hwZQ\", \"saschpe\".base64UrlEncoded)\n    }\n\n    @Test\n    fun string_roundTrip() {\n        assertEquals(\n            Base64Encoding.UrlSafe.alphabet,\n            Base64Encoding.UrlSafe.alphabet.base64UrlEncoded.base64UrlDecoded\n        )\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/wordle/WordleItemTest.kt",
    "content": "package com.infinitepower.newquiz.model.wordle\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.api.Test\n\ninternal class WordleItemTest {\n    @Test\n    fun isCompleted() {\n        val empty = WordleItem.Empty\n        assertThat(empty.isCompleted).isFalse()\n\n        val none = WordleItem.None(WordleChar('A'))\n        assertThat(none.isCompleted).isTrue()\n\n        val present = WordleItem.Present(WordleChar('A'))\n        assertThat(present.isCompleted).isTrue()\n\n        val correct = WordleItem.Correct(WordleChar('A'))\n        assertThat(correct.isCompleted).isTrue()\n    }\n\n    @Test\n    fun isVerified() {\n        val empty = WordleItem.Empty\n        assertThat(empty.isVerified).isFalse()\n\n        val none = WordleItem.None(WordleChar('A'))\n        assertThat(none.isVerified).isFalse()\n\n        val none2 = WordleItem.None(WordleChar('A'), true)\n        assertThat(none2.isVerified).isTrue()\n\n        val present = WordleItem.Present(WordleChar('A'))\n        assertThat(present.isVerified).isTrue()\n\n        val correct = WordleItem.Correct(WordleChar('A'))\n        assertThat(correct.isVerified).isTrue()\n    }\n\n    @Test\n    fun `list wordle item count by type`() {\n        val items = listOf(\n            WordleItem.Empty,\n            WordleItem.None(WordleChar('A')),\n            WordleItem.None(WordleChar('A')),\n            WordleItem.Present(WordleChar('A')),\n            WordleItem.Present(WordleChar('A')),\n            WordleItem.Present(WordleChar('A')),\n            WordleItem.Correct(WordleChar('A')),\n            WordleItem.Correct(WordleChar('A')),\n            WordleItem.Correct(WordleChar('A')),\n            WordleItem.Correct(WordleChar('A'))\n        )\n\n        assertThat(items.countByItem<WordleItem.Empty>()).isEqualTo(1)\n        assertThat(items.countByItem<WordleItem.None>()).isEqualTo(2)\n        assertThat(items.countByItem<WordleItem.Present>()).isEqualTo(3)\n        assertThat(items.countByItem<WordleItem.Correct>()).isEqualTo(4)\n    }\n}\n"
  },
  {
    "path": "model/src/test/java/com/infinitepower/newquiz/model/wordle/WordleRowItemTest.kt",
    "content": "package com.infinitepower.newquiz.model.wordle\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.jupiter.api.Test\n\ninternal class WordleRowItemTest {\n\n    @Test\n    fun isRowCorrect() {\n        val rowItem1 = WordleRowItem(items = listOf(WordleItem.Empty))\n        assertThat(rowItem1.isRowCorrect).isFalse()\n\n        val rowItem2 = WordleRowItem(items = listOf(WordleItem.fromChar('A')))\n        assertThat(rowItem2.isRowCorrect).isFalse()\n\n        val rowItem3 = WordleRowItem(\n            items = listOf(\n                WordleItem.fromChar('A'),\n                WordleItem.Present(WordleChar('B')),\n                WordleItem.Correct(WordleChar('C'))\n            )\n        )\n        assertThat(rowItem3.isRowCorrect).isFalse()\n\n        val rowItem4 = WordleRowItem(\n            items = listOf(\n                WordleItem.fromChar('A'),\n                WordleItem.Empty,\n                WordleItem.Present(WordleChar('B')),\n                WordleItem.Correct(WordleChar('C'))\n            )\n        )\n        assertThat(rowItem4.isRowCorrect).isFalse()\n\n        val rowItem5 = WordleRowItem(items = listOf(WordleItem.Correct(WordleChar('C'))))\n        assertThat(rowItem5.isRowCorrect).isTrue()\n    }\n\n    @Test\n    fun isRowCompleted() {\n        val rowItem1 = WordleRowItem(items = listOf(WordleItem.Empty))\n        assertThat(rowItem1.isRowCompleted).isFalse()\n\n        val rowItem2 = WordleRowItem(items = listOf(WordleItem.fromChar('A')))\n        assertThat(rowItem2.isRowCompleted).isTrue()\n\n        val rowItem3 = WordleRowItem(\n            items = listOf(\n                WordleItem.fromChar('A'),\n                WordleItem.Present(WordleChar('B')),\n                WordleItem.Correct(WordleChar('C'))\n            )\n        )\n        assertThat(rowItem3.isRowCompleted).isTrue()\n\n        val rowItem4 = WordleRowItem(\n            items = listOf(\n                WordleItem.fromChar('A'),\n                WordleItem.Empty,\n                WordleItem.Present(WordleChar('B')),\n                WordleItem.Correct(WordleChar('C'))\n            )\n        )\n        assertThat(rowItem4.isRowCompleted).isFalse()\n    }\n\n    @Test\n    fun isRowVerified() {\n        val rowItem1 = WordleRowItem(items = listOf(WordleItem.Empty))\n        assertThat(rowItem1.isRowVerified).isFalse()\n\n        val rowItem2 = WordleRowItem(items = listOf(WordleItem.fromChar('A')))\n        assertThat(rowItem2.isRowVerified).isFalse()\n\n        val rowItem3 = WordleRowItem(\n            items = listOf(\n                WordleItem.fromChar('A'),\n                WordleItem.Present(WordleChar('B')),\n                WordleItem.Correct(WordleChar('C'))\n            )\n        )\n        assertThat(rowItem3.isRowVerified).isFalse()\n\n        val rowItem4 = WordleRowItem(\n            items = listOf(\n                WordleItem.fromChar('A'),\n                WordleItem.Empty,\n                WordleItem.Present(WordleChar('B')),\n                WordleItem.Correct(WordleChar('C'))\n            )\n        )\n        assertThat(rowItem4.isRowVerified).isFalse()\n\n        val rowItem5 = WordleRowItem(\n            items = listOf(\n                WordleItem.None(WordleChar('A'), true),\n                WordleItem.Present(WordleChar('B')),\n                WordleItem.Correct(WordleChar('C'))\n            )\n        )\n        assertThat(rowItem5.isRowVerified).isTrue()\n\n        val rowItem6 = WordleRowItem(items = listOf(WordleItem.Present(WordleChar('A'))))\n        assertThat(rowItem6.isRowVerified).isTrue()\n\n        val rowItem7 = WordleRowItem(items = listOf(WordleItem.Correct(WordleChar('A'))))\n        assertThat(rowItem7.isRowVerified).isTrue()\n    }\n}\n"
  },
  {
    "path": "multi-choice-quiz/.gitignore",
    "content": "/build"
  },
  {
    "path": "multi-choice-quiz/README.md",
    "content": "# Multi choice quiz\n\nThis is the code for the multi choice quiz game mode.\n\n![NewQuiz purple light](../pictures/multichoicequiz.jpg)"
  },
  {
    "path": "multi-choice-quiz/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library.compose)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n    alias(libs.plugins.newquiz.kotlin.serialization)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.multi_choice_quiz\"\n}\n\ndependencies {\n    implementation(libs.androidx.core.ktx)\n\n    implementation(libs.androidx.lifecycle.livedata.ktx)\n    implementation(libs.androidx.lifecycle.runtimeCompose)\n\n    implementation(libs.androidx.compose.ui.tooling)\n    implementation(libs.androidx.compose.ui.tooling.preview)\n    implementation(libs.androidx.compose.material3)\n    implementation(libs.androidx.compose.material3.windowSizeClass)\n    implementation(libs.androidx.compose.material.iconsExtended)\n    debugImplementation(libs.androidx.compose.ui.testManifest)\n    implementation(libs.androidx.constraintlayout.compose)\n\n    androidTestImplementation(libs.androidx.compose.ui.test)\n    androidTestImplementation(libs.androidx.test.runner)\n    androidTestImplementation(libs.androidx.test.rules)\n    androidTestImplementation(libs.androidx.work.testing)\n\n    implementation(libs.hilt.navigationCompose)\n\n    implementation(libs.room.runtime)\n    ksp(libs.room.compiler)\n    implementation(libs.room.ktx)\n    implementation(libs.room.testing)\n\n    implementation(libs.lottie.compose)\n\n    implementation(libs.coil.kt.compose)\n    implementation(libs.coil.kt.svg)\n\n    implementation(libs.androidx.work.ktx)\n\n    implementation(projects.core)\n    implementation(projects.core.analytics)\n    implementation(projects.core.datastore)\n    implementation(projects.core.translation)\n    implementation(projects.core.userServices)\n    implementation(projects.core.remoteConfig)\n    implementation(projects.model)\n    implementation(projects.domain)\n    implementation(projects.data)\n    androidTestImplementation(projects.core.testing)\n}\n"
  },
  {
    "path": "multi-choice-quiz/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "multi-choice-quiz/proguard-rules.pro",
    "content": "# Keep `Companion` object fields of serializable classes.\n# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.\n-if @kotlinx.serialization.Serializable class **\n-keepclassmembers class <1> {\n    static <1>$Companion Companion;\n}\n\n# Keep `serializer()` on companion objects (both default and named) of serializable classes.\n-if @kotlinx.serialization.Serializable class ** {\n    static **$* *;\n}\n-keepclassmembers class <2>$<3> {\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n    public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n    public static <1> INSTANCE;\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.\n-keepattributes RuntimeVisibleAnnotations,AnnotationDefault\n\n-keepclassmembers @kotlinx.serialization.Serializable class ** {\n    *** Companion;\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n    public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n    public static <1> INSTANCE;\n    kotlinx.serialization.KSerializer serializer(...);\n}\n\n-keep class kotlin.reflect.** { *; }\n-dontwarn kotlin.reflect.**\n-keep class org.jetbrains.** { *; }\n\n-dontwarn java.lang.invoke.StringConcatFactory\n\n-dontwarn kotlin.**\n-dontwarn org.w3c.dom.events.*\n-dontwarn org.jetbrains.kotlin.di.InjectorForRuntimeDescriptorLoader\n\n-keepattributes SourceFile,LineNumberTable\n\n-keep class kotlin.** { *; }\n#-keep class kotlin.reflect.** { *; }\n#-keep class org.jetbrains.kotlin.** { *; }\n\n-keepclassmembers,allowoptimization enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n    **[] $VALUES;\n    public *;\n}\n\n-keepattributes InnerClasses\n\n# Ktor\n-keep class io.ktor.** { *; }\n-keep class kotlinx.coroutines.** { *; }\n-dontwarn kotlinx.atomicfu.**\n-dontwarn io.netty.**\n-dontwarn com.typesafe.**\n-dontwarn org.slf4j.**\n\n-keepattributes *Annotation*, InnerClasses\n-dontnote kotlinx.serialization.SerializationKt\n-keep,includedescriptorclasses class com.infinitepower.newsocial.compose.**$$serializer { *; }\n\n-keep class kotlin.reflect.** { *; }\n-dontwarn kotlin.reflect.**\n-keep class org.jetbrains.** { *; }"
  },
  {
    "path": "multi-choice-quiz/src/androidTest/java/com/infinitepower/newquiz/multi_choice_quiz/MultiChoiceQuizScreenTest.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz\n\nimport androidx.activity.ComponentActivity\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.calculateWindowSizeClass\nimport androidx.compose.ui.test.ExperimentalTestApi\nimport androidx.compose.ui.test.assertAll\nimport androidx.compose.ui.test.assertContentDescriptionEquals\nimport androidx.compose.ui.test.assertCountEquals\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.assertIsEnabled\nimport androidx.compose.ui.test.assertIsSelected\nimport androidx.compose.ui.test.assertTextEquals\nimport androidx.compose.ui.test.hasContentDescription\nimport androidx.compose.ui.test.hasText\nimport androidx.compose.ui.test.isEnabled\nimport androidx.compose.ui.test.isNotEnabled\nimport androidx.compose.ui.test.isNotSelected\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.compose.ui.test.onChildren\nimport androidx.compose.ui.test.onFirst\nimport androidx.compose.ui.test.onNodeWithContentDescription\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.work.WorkManager\nimport androidx.work.testing.WorkManagerTestInitHelper\nimport com.infinitepower.newquiz.core.analytics.LocalDebugAnalyticsHelper\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport com.infinitepower.newquiz.core.translation.TranslatorUtil\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.MultiChoiceQuestionRepository\nimport com.infinitepower.newquiz.domain.use_case.question.GetRandomMultiChoiceQuestionUseCase\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport io.mockk.clearAllMocks\nimport io.mockk.coEvery\nimport io.mockk.mockk\nimport org.junit.Rule\nimport org.junit.runner.RunWith\nimport javax.inject.Inject\nimport kotlin.test.AfterTest\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n/**\n * Tests for [MultiChoiceQuizScreen].\n */\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\n@OptIn(\n    ExperimentalTestApi::class,\n    ExperimentalMaterial3WindowSizeClassApi::class\n)\ninternal class MultiChoiceQuizScreenTest {\n    @get:Rule\n    val hiltRule = HiltAndroidRule(this)\n\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<ComponentActivity>()\n\n    private lateinit var workManager: WorkManager\n\n    @Inject\n    lateinit var getRandomQuestionUseCase: GetRandomMultiChoiceQuestionUseCase\n\n    @Inject\n    lateinit var userService: UserService\n\n    @Inject\n    lateinit var remoteConfig: RemoteConfig\n\n    private val settingsDataStoreManager = mockk<DataStoreManager>()\n    private val translationUtil = mockk<TranslatorUtil>()\n\n    @BeforeTest\n    fun setUp() {\n        hiltRule.inject()\n\n        remoteConfig.initialize()\n\n        WorkManagerTestInitHelper.initializeTestWorkManager(composeTestRule.activity)\n        workManager = WorkManager.getInstance(composeTestRule.activity)\n\n        val questionsRepository = mockk<MultiChoiceQuestionRepository>()\n\n        coEvery {\n            questionsRepository.getRandomQuestions(any(), any(), any())\n        } returns List(5) {\n            getBasicMultiChoiceQuestion(\n                correctAns = 0\n            )\n        }\n    }\n\n    @AfterTest\n    fun tearDown() {\n        clearAllMocks()\n    }\n\n    @Test\n    fun testMultiChoiceQuizScreen() {\n        coEvery {\n            settingsDataStoreManager.getPreference(SettingsCommon.MultiChoiceQuizQuestionsSize)\n        } returns 5\n\n        coEvery { translationUtil.isReadyToTranslate() } returns false\n\n        composeTestRule.setTestContent {\n            val windowSizeClass = calculateWindowSizeClass(composeTestRule.activity)\n\n            MultiChoiceQuizScreen(\n                navigator = EmptyDestinationsNavigator,\n                windowSizeClass = windowSizeClass,\n                viewModel = QuizScreenViewModel(\n                    getRandomQuestionUseCase = getRandomQuestionUseCase,\n                    settingsDataStoreManager = settingsDataStoreManager,\n                    savedQuestionsRepository = mockk(),\n                    recentCategoriesRepository = mockk(),\n                    savedStateHandle = SavedStateHandle(\n                        mapOf(\n                            \"category\" to MultiChoiceBaseCategory.Random\n                        )\n                    ),\n                    translationUtil = translationUtil,\n                    workManager = workManager,\n                    isQuestionSavedUseCase = mockk(relaxed = true),\n                    analyticsHelper = LocalDebugAnalyticsHelper(),\n                    userService = userService,\n                    remoteConfig = remoteConfig\n                )\n            )\n        }\n\n        composeTestRule.waitUntilDoesNotExist(hasText(\"Loading...\"))\n\n        // Test quiz step row\n        composeTestRule\n            .onNodeWithContentDescription(\"Quiz steps container\")\n            .assertIsDisplayed()\n            .onChildren()\n            .assertCountEquals(5)\n            .assertAll(isNotEnabled())\n            .onFirst()\n            .assertTextEquals(\"1\")\n\n        for (questionNumber in 1..5) {\n            composeTestRule\n                .onNodeWithText(\"Question $questionNumber/5\")\n                .assertIsDisplayed()\n\n            // Test question description\n            composeTestRule.onNodeWithText(\"Question ${questionNumber - 1}\").assertIsDisplayed()\n\n            // Test question answers\n            composeTestRule\n                .onNodeWithContentDescription(\"Answers container\")\n                .assertIsDisplayed()\n                .onChildren()\n                .assertCountEquals(4)\n                .assertAll(isEnabled() and isNotSelected())\n\n            // Test verify button\n            composeTestRule.onNodeWithText(\"Verify\").assertDoesNotExist()\n\n            val randomAnswer = (0..3).random()\n            val randomAnswerText = \"Answer $randomAnswer\"\n\n            // Click on random answer\n            composeTestRule\n                .onNodeWithText(randomAnswerText)\n                .assertIsDisplayed()\n                .assertIsEnabled()\n                .performClick()\n                .assertIsEnabled()\n                .assertIsSelected()\n\n            // Check if verify button is displayed after selecting an answer\n            composeTestRule\n                .onNode(hasText(\"Verify\") or hasContentDescription(\"Verify\"))\n                .assertIsDisplayed()\n                .assertIsEnabled()\n                .performClick() // Click on verify button\n                .assertDoesNotExist() // Check if verify button is not displayed after clicking on it\n\n            // Check if correct answer is displayed in the steps\n            composeTestRule\n                .onNodeWithContentDescription(\"Quiz steps container\")\n                .assertIsDisplayed()\n                .onChildren()\n                .assertAll(isNotEnabled())\n        }\n    }\n}\n"
  },
  {
    "path": "multi-choice-quiz/src/androidTest/java/com/infinitepower/newquiz/multi_choice_quiz/components/CardQuestionOptionTest.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.*\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport com.infinitepower.newquiz.model.multi_choice_quiz.SelectedAnswer\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\nclass CardQuestionOptionTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun testButtonClick_whenNotSelected() {\n        var clicked by mutableStateOf(false)\n\n        composeTestRule.setTestContent {\n            CardQuestionAnswer(\n                answer = \"Test\",\n                selected = clicked,\n                onClick = { clicked = true }\n            )\n        }\n\n        composeTestRule\n            .onNodeWithText(\"Test\")\n            .assertHasClickAction()\n            .assertIsNotSelected()\n            .performClick()\n            .assertIsSelected()\n\n        assertThat(clicked).isTrue()\n    }\n\n    @Test\n    fun testButtonClick_whenSelected() {\n        var clicked by mutableStateOf(true)\n\n        composeTestRule.setTestContent {\n            CardQuestionAnswer(\n                answer = \"Test\",\n                selected = clicked,\n                onClick = { clicked = true }\n            )\n        }\n\n        composeTestRule\n            .onNodeWithText(\"Test\")\n            .assertHasClickAction()\n            .assertIsSelected()\n            .performClick()\n            .assertIsSelected()\n\n        assertThat(clicked).isTrue()\n    }\n\n    @Test\n    fun testButtonsClick() {\n        val answers = listOf(\"A\", \"B\", \"C\", \"D\")\n\n        var selectedAnswer: SelectedAnswer by mutableStateOf(SelectedAnswer.NONE)\n\n        composeTestRule.setTestContent {\n            CardQuestionAnswers(\n                modifier = Modifier.testTag(\"Answers\"),\n                answers = answers,\n                selectedAnswer = selectedAnswer,\n                onOptionClick = { selectedAnswer = it }\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"Answers\")\n            .assertContentDescriptionEquals(\"Answers container\")\n            .onChildren()\n            .assertCountEquals(answers.size)\n            .assertAll(isNotSelected() and hasClickAction())\n            .onFirst()\n            .performClick()\n            .assertIsSelected()\n\n        composeTestRule\n            .onNodeWithTag(\"Answers\")\n            .onChildren()\n            .filterToOne(isSelected())\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/androidTest/java/com/infinitepower/newquiz/multi_choice_quiz/components/QuizStepViewRowTest.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components\n\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertContentDescriptionEquals\nimport androidx.compose.ui.test.assertCountEquals\nimport androidx.compose.ui.test.assertTextEquals\nimport androidx.compose.ui.test.filter\nimport androidx.compose.ui.test.hasClickAction\nimport androidx.compose.ui.test.isEnabled\nimport androidx.compose.ui.test.isNotEnabled\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onChildren\nimport androidx.compose.ui.test.onFirst\nimport androidx.compose.ui.test.onLast\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.performClick\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Tests for [QuizStepViewRow]\n */\n@SmallTest\n@RunWith(AndroidJUnit4::class)\nclass QuizStepViewRowTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun quizStepViewRow_WithQuestionSteps_whenGameScreen() {\n        var clicked = false\n\n        val questionSteps = listOf(\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = false\n            ),\n            MultiChoiceQuestionStep.Current(question = getBasicMultiChoiceQuestion()),\n            MultiChoiceQuestionStep.NotCurrent(question = getBasicMultiChoiceQuestion())\n        )\n\n        composeTestRule.setTestContent {\n            QuizStepViewRow(\n                questionSteps = questionSteps,\n                isResultsScreen = false, // Game screen\n                onClick = { _, _ -> clicked = true },\n                modifier = Modifier.testTag(\"QuizStepViewRow\")\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"QuizStepViewRow\")\n            .onChildren()\n            .filter(isNotEnabled())\n            .assertCountEquals(4)\n            .apply {\n                onFirst().assertContentDescriptionEquals(\"Question 1 - Correct\")\n                this[1].assertContentDescriptionEquals(\"Question 2 - Incorrect\")\n                this[2].assertTextEquals(\"3\")\n                onLast()\n                    .assertTextEquals(\"4\")\n                    .performClick()\n            }\n\n        assertThat(clicked).isFalse()\n    }\n\n    @Test\n    fun quizStepViewRow_WithQuestionSteps_whenResultsScreen() {\n        var clickedIndex = -1\n        var clickedQuestionStep: MultiChoiceQuestionStep? = null\n\n        val questionSteps = listOf(\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = true\n            ),\n            MultiChoiceQuestionStep.Completed(\n                question = getBasicMultiChoiceQuestion(),\n                correct = false\n            ),\n            MultiChoiceQuestionStep.Current(question = getBasicMultiChoiceQuestion()),\n            MultiChoiceQuestionStep.NotCurrent(question = getBasicMultiChoiceQuestion())\n        )\n\n        composeTestRule.setTestContent {\n            QuizStepViewRow(\n                questionSteps = questionSteps,\n                isResultsScreen = true, // Results screen\n                onClick = { index, questionStep ->\n                    clickedIndex = index\n                    clickedQuestionStep = questionStep\n                },\n                modifier = Modifier.testTag(\"QuizStepViewRow\")\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"QuizStepViewRow\")\n            .onChildren()\n            .filter(isEnabled() and hasClickAction())\n            .assertCountEquals(4)\n            .apply {\n                onFirst().assertContentDescriptionEquals(\"Question 1 - Correct\")\n                this[1].assertContentDescriptionEquals(\"Question 2 - Incorrect\")\n                this[2].assertTextEquals(\"3\")\n                onLast()\n                    .assertTextEquals(\"4\")\n                    .performClick()\n            }\n\n        assertThat(clickedIndex).isEqualTo(3)\n        assertThat(clickedQuestionStep).isEqualTo(questionSteps[3])\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/androidTest/java/com/infinitepower/newquiz/multi_choice_quiz/components/QuizStepViewTest.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components\n\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertContentDescriptionEquals\nimport androidx.compose.ui.test.assertHasClickAction\nimport androidx.compose.ui.test.assertHeightIsEqualTo\nimport androidx.compose.ui.test.assertIsEnabled\nimport androidx.compose.ui.test.assertIsNotEnabled\nimport androidx.compose.ui.test.assertTextEquals\nimport androidx.compose.ui.test.assertWidthIsEqualTo\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.compose.ui.unit.dp\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Tests for [QuizStepView]\n */\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class QuizStepViewTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun quizStepView_whenCompletedCorrectAnswer_showsCheckIcon() {\n        var clicked = false\n\n        composeTestRule.setTestContent {\n            QuizStepView(\n                questionStep = MultiChoiceQuestionStep.Completed(\n                    question = getBasicMultiChoiceQuestion(),\n                    correct = true\n                ),\n                position = 1,\n                enabled = false,\n                onClick = { clicked = true },\n                modifier = Modifier.testTag(\"QuizStepView\")\n            )\n        }\n\n        composeTestRule.onNodeWithText(\"1\").assertDoesNotExist()\n\n        composeTestRule\n            .onNodeWithTag(\"QuizStepView\")\n            .assertIsNotEnabled()\n            .assertWidthIsEqualTo(35.dp)\n            .assertHeightIsEqualTo(35.dp)\n            .assertContentDescriptionEquals(\"Question 1 - Correct\")\n\n        assertThat(clicked).isFalse()\n    }\n\n    @Test\n    fun quizStepView_whenCompletedIncorrectAnswer_showsCloseIcon() {\n        composeTestRule.setTestContent {\n            QuizStepView(\n                questionStep = MultiChoiceQuestionStep.Completed(\n                    question = getBasicMultiChoiceQuestion(),\n                    correct = false\n                ),\n                position = 1,\n                enabled = false,\n                onClick = {},\n                modifier = Modifier.testTag(\"QuizStepView\")\n            )\n        }\n\n        composeTestRule.onNodeWithText(\"1\").assertDoesNotExist()\n\n        composeTestRule\n            .onNodeWithTag(\"QuizStepView\")\n            .assertIsNotEnabled()\n            .assertWidthIsEqualTo(35.dp)\n            .assertHeightIsEqualTo(35.dp)\n            .assertContentDescriptionEquals(\"Question 1 - Incorrect\")\n    }\n\n    @Test\n    fun quizStepView_whenCurrentStep_showsPosition() {\n        composeTestRule.setTestContent {\n            QuizStepView(\n                questionStep = MultiChoiceQuestionStep.Current(\n                    question = getBasicMultiChoiceQuestion()\n                ),\n                position = 1,\n                enabled = false,\n                onClick = {},\n                modifier = Modifier.testTag(\"QuizStepView\")\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"QuizStepView\")\n            .assertTextEquals(\"1\")\n            .assertIsNotEnabled()\n            .assertWidthIsEqualTo(35.dp)\n            .assertHeightIsEqualTo(35.dp)\n    }\n\n    @Test\n    fun quizStepView_whenNotCurrentStep_showsPosition() {\n        composeTestRule.setTestContent {\n            QuizStepView(\n                questionStep = MultiChoiceQuestionStep.NotCurrent(\n                    question = getBasicMultiChoiceQuestion()\n                ),\n                position = 1,\n                enabled = false,\n                onClick = {},\n                modifier = Modifier.testTag(\"QuizStepView\")\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"QuizStepView\")\n            .assertTextEquals(\"1\")\n            .assertIsNotEnabled()\n            .assertWidthIsEqualTo(35.dp)\n            .assertHeightIsEqualTo(35.dp)\n    }\n\n    @Test\n    fun quizStepView_whenEnabled_shouldClick() {\n        var clicked = false\n\n        composeTestRule.setTestContent {\n            QuizStepView(\n                questionStep = MultiChoiceQuestionStep.Completed(\n                    question = getBasicMultiChoiceQuestion(),\n                    correct = true\n                ),\n                position = 1,\n                enabled = true,\n                onClick = { clicked = true },\n                modifier = Modifier.testTag(\"QuizStepView\")\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(\"QuizStepView\")\n            .assertIsEnabled()\n            .assertHasClickAction()\n            .assertWidthIsEqualTo(35.dp)\n            .assertHeightIsEqualTo(35.dp)\n            .assertContentDescriptionEquals(\"Question 1 - Correct\")\n            .performClick()\n\n        assertThat(clicked).isTrue()\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n</manifest>"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/MultiChoiceQuizScreen.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz\n\nimport androidx.annotation.Keep\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.aspectRatio\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.windowInsetsPadding\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBarDefaults\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.WindowSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowWidthSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalConfiguration\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.tooling.preview.PreviewScreenSizes\nimport androidx.compose.ui.unit.DpSize\nimport androidx.compose.ui.unit.dp\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport androidx.navigation.NavController\nimport coil.ImageLoader\nimport coil.compose.AsyncImage\nimport coil.decode.SvgDecoder\nimport com.infinitepower.newquiz.core.navigation.MazeNavigator\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.ui.components.skip_question.SkipQuestionDialog\nimport com.infinitepower.newquiz.core.util.toAndroidUri\nimport com.infinitepower.newquiz.model.RemainingTime\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.SelectedAnswer\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport com.infinitepower.newquiz.multi_choice_quiz.components.CardQuestionAnswers\nimport com.infinitepower.newquiz.multi_choice_quiz.components.MultiChoiceQuizContainer\nimport com.infinitepower.newquiz.multi_choice_quiz.components.QuizStepViewRow\nimport com.infinitepower.newquiz.multi_choice_quiz.components.QuizTopBar\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizResultsScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizScreenDestination\nimport com.ramcosta.composedestinations.annotation.DeepLink\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport kotlin.time.Duration.Companion.seconds\n\ninternal val MULTI_CHOICE_QUIZ_COUNTDOWN_TIME = 30.seconds\n\n@Keep\ndata class MultiChoiceQuizScreenNavArg(\n    val initialQuestions: ArrayList<MultiChoiceQuestion> = arrayListOf(),\n    val category: MultiChoiceBaseCategory = MultiChoiceBaseCategory.Normal(),\n    val difficulty: String? = null,\n    val mazeItemId: String? = null\n)\n\n@Composable\n@Destination(\n    navArgsDelegate = MultiChoiceQuizScreenNavArg::class,\n    deepLinks = [\n        DeepLink(uriPattern = \"newquiz://quickquiz\")\n    ]\n)\n@OptIn(ExperimentalMaterial3Api::class)\nfun MultiChoiceQuizScreen(\n    navigator: DestinationsNavigator,\n    mazeNavigator: MazeNavigator,\n    navController: NavController,\n    windowSizeClass: WindowSizeClass,\n    viewModel: QuizScreenViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    LaunchedEffect(uiState.isGameEnded) {\n        if (uiState.isGameEnded) {\n            val backStackEntry = navController.getBackStackEntry(MultiChoiceQuizScreenDestination.route)\n            val args = MultiChoiceQuizScreenDestination.argsFrom(backStackEntry)\n\n            navigateToResultsScreen(\n                navigator = navigator,\n                mazeNavigator = mazeNavigator,\n                args = args,\n                questionSteps = uiState.questionSteps.filterIsInstance<MultiChoiceQuestionStep.Completed>()\n            )\n        }\n    }\n\n    MultiChoiceQuizScreenImpl(\n        onBackClick = navigator::popBackStack,\n        windowSizeClass = windowSizeClass,\n        uiState = uiState,\n        onEvent = viewModel::onEvent\n    )\n\n    SkipQuestionDialog(\n        userDiamonds = uiState.userDiamonds,\n        skipCost = uiState.skipCost,\n        loading = uiState.userDiamondsLoading,\n        onSkipClick = { viewModel.onEvent(MultiChoiceQuizScreenUiEvent.SkipQuestion) },\n        onDismissClick = { viewModel.onEvent(MultiChoiceQuizScreenUiEvent.CleanUserSkipQuestionDiamonds) }\n    )\n}\n\n@OptIn(ExperimentalMaterial3Api::class)\n@Composable\nprivate fun MultiChoiceQuizScreenImpl(\n    uiState: MultiChoiceQuizScreenUiState,\n    windowSizeClass: WindowSizeClass,\n    onBackClick: () -> Unit,\n    onEvent: (event: MultiChoiceQuizScreenUiEvent) -> Unit\n) {\n    val context = LocalContext.current\n\n    val imageLoader = ImageLoader\n        .Builder(context)\n        .components {\n            add(SvgDecoder.Factory())\n        }.build()\n\n    val widthCompact = windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact\n\n    val currentQuestion = uiState.currentQuestionStep?.question\n\n    MultiChoiceQuizContainer(\n        modifier = Modifier.fillMaxSize(),\n        loading = uiState.loading,\n        windowSizeClass = windowSizeClass,\n        answerSelected = uiState.selectedAnswer.isSelected,\n        onVerifyQuestionClick = { onEvent(MultiChoiceQuizScreenUiEvent.VerifyAnswer) },\n        topBarContent = {\n            QuizTopBar(\n                remainingTime = uiState.remainingTime,\n                windowHeightSizeClass = windowSizeClass.heightSizeClass,\n                skipsAvailable = uiState.skipsAvailable,\n                onBackClick = onBackClick,\n                onSkipClick = { onEvent(MultiChoiceQuizScreenUiEvent.GetUserSkipQuestionDiamonds) },\n                onSaveClick = { onEvent(MultiChoiceQuizScreenUiEvent.SaveQuestion) },\n                modifier = Modifier\n                    .windowInsetsPadding(TopAppBarDefaults.windowInsets)\n                    .fillMaxWidth(),\n                currentQuestionNull = uiState.currentQuestionStep == null,\n                questionSaved = uiState.questionSaved\n            )\n        },\n        stepsContent = {\n            QuizStepViewRow(\n                questionSteps = uiState.questionSteps,\n                isResultsScreen = false\n            )\n        },\n        questionPositionContent = {\n            Text(\n                text = uiState.getQuestionPositionFormatted(),\n                style = MaterialTheme.typography.headlineSmall\n            )\n        },\n        questionDescriptionContent = {\n            if (currentQuestion != null) {\n                Text(\n                    text = currentQuestion.description,\n                    style = MaterialTheme.typography.bodyLarge\n                )\n            }\n        },\n        questionImageContent = {\n            currentQuestion?.image?.let { image ->\n                val imageScale = if (currentQuestion.category == MultiChoiceBaseCategory.Logo) {\n                    ContentScale.FillHeight\n                } else ContentScale.Crop\n\n                val maxWidth = if (widthCompact) 1f else 0.4f\n\n                Box(\n                    modifier = Modifier.fillMaxWidth(maxWidth)\n                ) {\n                    AsyncImage(\n                        model = image.toAndroidUri(),\n                        contentDescription = \"Image\",\n                        modifier = Modifier\n                            .aspectRatio(16 / 9f)\n                            .clip(MaterialTheme.shapes.medium),\n                        contentScale = imageScale,\n                        imageLoader = imageLoader\n                    )\n                }\n            }\n        },\n        answersContent = {\n            CardQuestionAnswers(\n                answers = currentQuestion?.answers.orEmpty(),\n                selectedAnswer = uiState.selectedAnswer,\n                onOptionClick = { answer ->\n                    onEvent(MultiChoiceQuizScreenUiEvent.SelectAnswer(answer))\n                }\n            )\n        }\n    )\n}\n\nprivate fun navigateToResultsScreen(\n    navigator: DestinationsNavigator,\n    mazeNavigator: MazeNavigator,\n    args: MultiChoiceQuizScreenNavArg,\n    questionSteps: List<MultiChoiceQuestionStep.Completed>\n) {\n    val questionFromMaze = args.mazeItemId != null\n\n    if (questionFromMaze) {\n        mazeNavigator.navigateToMazeResults(args.mazeItemId?.toIntOrNull() ?: return)\n    } else {\n        val questionStepsStr = Json.encodeToString(questionSteps)\n\n        navigator.navigate(\n            MultiChoiceQuizResultsScreenDestination(\n                questionStepsStr = questionStepsStr,\n                byInitialQuestions = args.initialQuestions.isNotEmpty(),\n                category = args.category,\n                difficulty = args.difficulty\n            )\n        ) {\n            launchSingleTop = true\n\n            popUpTo(MultiChoiceQuizScreenDestination) {\n                inclusive = true\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewScreenSizes\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)\nprivate fun QuizScreenPreview() {\n    val questionSteps = listOf(\n        MultiChoiceQuestionStep.Completed(\n            question = getBasicMultiChoiceQuestion(),\n            correct = true\n        ),\n        MultiChoiceQuestionStep.Completed(\n            question = getBasicMultiChoiceQuestion(),\n            correct = false\n        ),\n        MultiChoiceQuestionStep.Current(question = getBasicMultiChoiceQuestion()),\n        MultiChoiceQuestionStep.NotCurrent(question = getBasicMultiChoiceQuestion()),\n    )\n\n    val configuration = LocalConfiguration.current\n    val screenHeight = configuration.screenHeightDp.dp\n    val screenWidth = configuration.screenWidthDp.dp\n    val windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(screenWidth, screenHeight))\n\n    NewQuizTheme {\n        MultiChoiceQuizScreenImpl(\n            uiState = MultiChoiceQuizScreenUiState(\n                questionSteps = questionSteps,\n                selectedAnswer = SelectedAnswer.fromIndex((0..3).random()),\n                currentQuestionIndex = 2,\n                loading = false,\n                remainingTime = RemainingTime(20.seconds)\n            ),\n            windowSizeClass = windowSizeClass,\n            onBackClick = {},\n            onEvent = {},\n        )\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/MultiChoiceQuizScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.SelectedAnswer\n\nsealed class MultiChoiceQuizScreenUiEvent {\n    data class SelectAnswer(val answer: SelectedAnswer) : MultiChoiceQuizScreenUiEvent()\n\n    object VerifyAnswer : MultiChoiceQuizScreenUiEvent()\n\n    object SaveQuestion : MultiChoiceQuizScreenUiEvent()\n\n    object GetUserSkipQuestionDiamonds : MultiChoiceQuizScreenUiEvent()\n\n    object CleanUserSkipQuestionDiamonds : MultiChoiceQuizScreenUiEvent()\n\n    object SkipQuestion : MultiChoiceQuizScreenUiEvent()\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/MultiChoiceQuizScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.RemainingTime\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.SelectedAnswer\nimport com.infinitepower.newquiz.model.multi_choice_quiz.isAllCompleted\n\n@Keep\ndata class MultiChoiceQuizScreenUiState(\n    val loading: Boolean = true,\n    val questionSteps: List<MultiChoiceQuestionStep> = emptyList(),\n    val currentQuestionIndex: Int = -1,\n    val selectedAnswer: SelectedAnswer = SelectedAnswer.NONE,\n    val questionSaved: Boolean = false,\n    val remainingTime: RemainingTime = RemainingTime.ZERO,\n    val userDiamonds: Int = -1,\n    val userDiamondsLoading: Boolean = false,\n    val skipsAvailable: Boolean = false,\n    val skipCost: Int = 1,\n) {\n    val currentQuestionStep: MultiChoiceQuestionStep.Current? = questionSteps.getOrNull(currentQuestionIndex)?.asCurrent()\n\n    fun getQuestionPositionFormatted(): String =\n        \"Question ${currentQuestionIndex + 1}/${questionSteps.size}\"\n\n    /**\n     * Gets new question index.\n     * If question is the last question returns -1.\n     * @return new question index\n     */\n    fun getNextIndex(): Int = if (currentQuestionIndex == questionSteps.lastIndex) -1 else currentQuestionIndex + 1\n\n    val isGameEnded: Boolean\n        get() = !loading && questionSteps.isAllCompleted()\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/MultiChoiceQuizScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz\n\nimport android.os.CountDownTimer\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport androidx.work.OneTimeWorkRequestBuilder\nimport androidx.work.WorkManager\nimport androidx.work.workDataOf\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.datastore.common.SettingsCommon\nimport com.infinitepower.newquiz.core.datastore.di.SettingsDataStoreManager\nimport com.infinitepower.newquiz.core.datastore.manager.DataStoreManager\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfig\nimport com.infinitepower.newquiz.core.remote_config.RemoteConfigValue\nimport com.infinitepower.newquiz.core.remote_config.get\nimport com.infinitepower.newquiz.core.translation.TranslatorUtil\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.core.user_services.workers.MultiChoiceQuizEndGameWorker\nimport com.infinitepower.newquiz.data.worker.UpdateGlobalEventDataWorker\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport com.infinitepower.newquiz.domain.use_case.question.GetRandomMultiChoiceQuestionUseCase\nimport com.infinitepower.newquiz.domain.use_case.question.IsQuestionSavedUseCase\nimport com.infinitepower.newquiz.model.RemainingTime\nimport com.infinitepower.newquiz.model.Resource\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.SelectedAnswer\nimport com.infinitepower.newquiz.model.multi_choice_quiz.isAllCorrect\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.distinctUntilChangedBy\nimport kotlinx.coroutines.flow.emptyFlow\nimport kotlinx.coroutines.flow.filter\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.flow.flatMapLatest\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport javax.inject.Inject\n\nconst val QUIZ_COUNTDOWN_IN_MILLIS = 30000L\n\n@HiltViewModel\n@OptIn(ExperimentalCoroutinesApi::class)\nclass QuizScreenViewModel @Inject constructor(\n    private val getRandomQuestionUseCase: GetRandomMultiChoiceQuestionUseCase,\n    @SettingsDataStoreManager private val settingsDataStoreManager: DataStoreManager,\n    private val savedQuestionsRepository: SavedMultiChoiceQuestionsRepository,\n    private val recentCategoriesRepository: RecentCategoriesRepository,\n    savedStateHandle: SavedStateHandle,\n    private val translationUtil: TranslatorUtil,\n    private val workManager: WorkManager,\n    private val isQuestionSavedUseCase: IsQuestionSavedUseCase,\n    private val analyticsHelper: AnalyticsHelper,\n    private val userService: UserService,\n    private val remoteConfig: RemoteConfig,\n    private val mazeQuizRepository: MazeQuizRepository\n) : ViewModel() {\n    private val navArgs: MultiChoiceQuizScreenNavArg = savedStateHandle.navArgs()\n\n    private val _uiState = MutableStateFlow(MultiChoiceQuizScreenUiState())\n    val uiState = _uiState.asStateFlow()\n\n    private val timer = object : CountDownTimer(QUIZ_COUNTDOWN_IN_MILLIS, 250) {\n        override fun onTick(millisUntilFinished: Long) {\n            _uiState.update { currentState ->\n                currentState.copy(remainingTime = RemainingTime.fromMilliseconds(millisUntilFinished))\n            }\n        }\n\n        override fun onFinish() {\n            verifyQuestion()\n        }\n    }\n\n    override fun onCleared() {\n        timer.cancel()\n        super.onCleared()\n    }\n\n    fun onEvent(event: MultiChoiceQuizScreenUiEvent) {\n        when (event) {\n            is MultiChoiceQuizScreenUiEvent.SelectAnswer -> selectAnswer(event.answer)\n            is MultiChoiceQuizScreenUiEvent.VerifyAnswer -> verifyQuestion()\n            is MultiChoiceQuizScreenUiEvent.SaveQuestion -> saveQuestion()\n            is MultiChoiceQuizScreenUiEvent.GetUserSkipQuestionDiamonds -> getUserDiamonds()\n            is MultiChoiceQuizScreenUiEvent.CleanUserSkipQuestionDiamonds -> {\n                _uiState.update { currentState ->\n                    currentState.copy(\n                        userDiamonds = -1,\n                        userDiamondsLoading = false\n                    )\n                }\n            }\n\n            is MultiChoiceQuizScreenUiEvent.SkipQuestion -> skipQuestion()\n        }\n    }\n\n    init {\n        viewModelScope.launch {\n            val initialQuestions = navArgs.initialQuestions\n\n            if (initialQuestions.isEmpty()) {\n                loadByCloudQuestions()\n            } else {\n                createQuestionSteps(initialQuestions, MultiChoiceBaseCategory.Normal())\n            }\n        }\n\n        viewModelScope.launch {\n            _uiState.update { currentState ->\n                currentState.copy(\n                    skipsAvailable = userService.userAvailable(),\n                    skipCost = remoteConfig.get(RemoteConfigValue.MULTICHOICE_SKIP_COST)\n                )\n            }\n        }\n\n        uiState\n            .distinctUntilChangedBy { it.currentQuestionStep?.question }\n            .filter { it.currentQuestionStep?.question != null }\n            .flatMapLatest { state ->\n                val question =\n                    state.currentQuestionStep?.question ?: return@flatMapLatest emptyFlow()\n\n                isQuestionSavedUseCase(question)\n            }.onEach { res ->\n                if (res.isSuccess()) {\n                    val questionSaved = res.data == true\n\n                    _uiState.update { currentState ->\n                        currentState.copy(questionSaved = questionSaved)\n                    }\n                }\n            }.launchIn(viewModelScope)\n    }\n\n    private fun skipQuestion() = viewModelScope.launch {\n        _uiState.update { currentState ->\n            if (currentState.userDiamonds < 1) return@launch\n\n            val currentQuestionStep = currentState.currentQuestionStep\n            val correctAnswer = currentQuestionStep?.question?.correctAns ?: return@launch\n\n            selectAnswer(SelectedAnswer.fromIndex(correctAnswer))\n            verifyQuestion(skipped = true)\n\n            userService.addRemoveDiamonds(-currentState.skipCost)\n            analyticsHelper.logEvent(\n                AnalyticsEvent.SpendDiamonds(\n                    currentState.skipCost,\n                    \"skip_multichoicequestion\"\n                )\n            )\n\n            currentState.copy(userDiamonds = -1)\n        }\n    }\n\n    private suspend fun loadByCloudQuestions() {\n        val questionSize =\n            settingsDataStoreManager.getPreference(SettingsCommon.MultiChoiceQuizQuestionsSize)\n\n        val category = navArgs.category\n        val difficulty = navArgs.difficulty\n\n        if (category.hasCategory) {\n            recentCategoriesRepository.addMultiChoiceCategory(category.id)\n        }\n\n        getRandomQuestionUseCase(questionSize, category, difficulty).collect { res ->\n            if (res is Resource.Success) {\n                createQuestionSteps(res.data.orEmpty(), category, difficulty)\n            }\n        }\n    }\n\n    private suspend fun List<MultiChoiceQuestion>.getOrTranslateQuestions(): List<MultiChoiceQuestion> {\n        // If is not ready to translate or translation not enabled, return the same list\n        if (!translationUtil.isReadyToTranslate()) return this\n\n        return map { question ->\n            question translateQuestionWith translationUtil\n        }\n    }\n\n    private suspend infix fun MultiChoiceQuestion.translateQuestionWith(translationUtil: TranslatorUtil): MultiChoiceQuestion {\n        return copy(\n            description = translationUtil.translate(description),\n            answers = translationUtil.translate(answers)\n        )\n    }\n\n    private suspend fun createQuestionSteps(\n        questions: List<MultiChoiceQuestion>,\n        category: MultiChoiceBaseCategory,\n        difficulty: String? = null\n    ) {\n        val questionSteps = questions\n            .getOrTranslateQuestions()\n            .map { question -> question.toQuestionStep() }\n\n        _uiState.update { currentState ->\n            currentState.copy(\n                questionSteps = questionSteps,\n                loading = false\n            )\n        }\n\n        viewModelScope.launch(Dispatchers.IO) {\n            // Update global event data, for daily challenge and achievements\n            val event = if (category.hasCategory) {\n                GameEvent.MultiChoice.PlayQuizWithCategory(category.id)\n            } else {\n                GameEvent.MultiChoice.PlayRandomQuiz\n            }\n\n            UpdateGlobalEventDataWorker.enqueueWork(workManager = workManager, event)\n\n            // Log game start\n            analyticsHelper.logEvent(\n                AnalyticsEvent.MultiChoiceGameStart(\n                    questionsSize = questionSteps.size,\n                    category = category.toString(),\n                    difficulty = difficulty\n                )\n            )\n        }\n\n        nextQuestion()\n    }\n\n    private fun nextQuestion() {\n        _uiState.update { currentState ->\n            val nextIndex = currentState.getNextIndex()\n\n            when {\n                currentState.isGameEnded -> {\n                    endGame(currentState.questionSteps.filterIsInstance<MultiChoiceQuestionStep.Completed>())\n\n                    currentState.copy(currentQuestionIndex = -1)\n                }\n\n                nextIndex == -1 -> currentState.copy(currentQuestionIndex = nextIndex)\n                else -> {\n                    timer.start()\n\n                    val newSteps = currentState\n                        .questionSteps\n                        .toMutableList()\n                        .apply {\n                            val step = currentState.questionSteps[nextIndex].asCurrent()\n                            set(nextIndex, step)\n                        }\n\n                    currentState.copy(\n                        questionSteps = newSteps,\n                        currentQuestionIndex = nextIndex\n                    )\n                }\n            }\n        }\n    }\n\n    private fun selectAnswer(answer: SelectedAnswer) {\n        _uiState.update { currentState ->\n            currentState.copy(selectedAnswer = answer)\n        }\n    }\n\n    private fun verifyQuestion(skipped: Boolean = false) {\n        timer.cancel()\n\n        _uiState.update { currentState ->\n            val steps = currentState\n                .questionSteps\n                .toMutableList()\n                .apply {\n                    val currentQuestionIndex = currentState.currentQuestionIndex\n                    val currentQuestionStep = currentState.currentQuestionStep\n\n                    val questionTime = currentState.remainingTime.getElapsedSeconds(\n                        MULTI_CHOICE_QUIZ_COUNTDOWN_TIME\n                    )\n\n                    if (currentQuestionStep != null) {\n                        val questionCorrect =\n                            currentState.selectedAnswer isCorrect currentQuestionStep.question\n\n                        // Update play question and get answers correct for global event data\n                        viewModelScope.launch(Dispatchers.IO) {\n                            val events = if (questionCorrect) {\n                                arrayOf(\n                                    GameEvent.MultiChoice.PlayQuestions,\n                                    GameEvent.MultiChoice.GetAnswersCorrect\n                                )\n                            } else {\n                                arrayOf(GameEvent.MultiChoice.PlayQuestions)\n                            }\n\n                            UpdateGlobalEventDataWorker.enqueueWork(\n                                workManager = workManager,\n                                event = events\n                            )\n                        }\n\n                        val completedQuestionStep = currentQuestionStep.changeToCompleted(\n                            correct = questionCorrect,\n                            selectedAnswer = currentState.selectedAnswer,\n                            questionTime = questionTime,\n                            skipped = skipped\n                        )\n                        set(currentQuestionIndex, completedQuestionStep)\n                    }\n                }\n\n            currentState.copy(\n                questionSteps = steps,\n                selectedAnswer = SelectedAnswer.NONE\n            )\n        }\n\n        nextQuestion()\n    }\n\n    private fun saveQuestion() = viewModelScope.launch(Dispatchers.IO) {\n        val currentQuestionStep = uiState.first().currentQuestionStep ?: return@launch\n        val currentQuestion = currentQuestionStep.question\n\n        savedQuestionsRepository.insertQuestions(currentQuestion)\n\n        analyticsHelper.logEvent(AnalyticsEvent.MultiChoiceSaveQuestion)\n    }\n\n    private fun endGame(questionSteps: List<MultiChoiceQuestionStep.Completed>) {\n        viewModelScope.launch(Dispatchers.IO) {\n            val questionStepsStr = Json.encodeToString(questionSteps)\n\n            val endGameWorkRequest = OneTimeWorkRequestBuilder<MultiChoiceQuizEndGameWorker>()\n                .setInputData(\n                    workDataOf(\n                        MultiChoiceQuizEndGameWorker.INPUT_QUESTION_STEPS to questionStepsStr,\n                        // Only generate XP if is not initial questions (saved questions)\n                        MultiChoiceQuizEndGameWorker.INPUT_GENERATE_XP to navArgs.initialQuestions.isEmpty(),\n                    )\n                ).build()\n\n\n            val allCorrect = questionSteps.isAllCorrect()\n            val mazeItemId = navArgs.mazeItemId?.toIntOrNull()\n\n            if (mazeItemId != null) {\n                analyticsHelper.logEvent(AnalyticsEvent.MazeItemPlayed(allCorrect))\n\n                if (allCorrect) {\n                    mazeQuizRepository.completeMazeItem(mazeItemId)\n                }\n            }\n\n            workManager.enqueue(endGameWorkRequest)\n\n            UpdateGlobalEventDataWorker.enqueueWork(\n                workManager = workManager,\n                GameEvent.MultiChoice.EndQuiz\n            )\n        }\n    }\n\n    private fun getUserDiamonds() = viewModelScope.launch(Dispatchers.IO) {\n        _uiState.update { currentState ->\n            currentState.copy(userDiamondsLoading = true)\n        }\n\n        val userDiamonds = userService.getUserDiamonds()\n\n        _uiState.update { currentState ->\n            currentState.copy(\n                userDiamonds = userDiamonds.toInt(),\n                userDiamondsLoading = false\n            )\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/components/CardQuestionAnswer.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components\n\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.animation.animateColorAsState\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.State\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.rememberUpdatedState\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Shape\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.CustomColor\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.extendedColors\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.multi_choice_quiz.SelectedAnswer\n\n@Composable\ninternal fun CardQuestionAnswers(\n    modifier: Modifier = Modifier,\n    answers: List<String>,\n    selectedAnswer: SelectedAnswer,\n    isResultsScreen: Boolean = false,\n    correctAnswer: SelectedAnswer = SelectedAnswer.NONE,\n    onOptionClick: (selectedAnswer: SelectedAnswer) -> Unit = {}\n) {\n    val spaceSmall = MaterialTheme.spacing.small\n\n    Column(\n        modifier = modifier.semantics {\n            contentDescription = \"Answers container\"\n        },\n        verticalArrangement = Arrangement.spacedBy(spaceSmall)\n    ) {\n        answers.forEachIndexed { index, answer ->\n            CardQuestionAnswer(\n                modifier = Modifier.fillMaxWidth(),\n                answer = answer,\n                selected = selectedAnswer.index == index,\n                isResults = isResultsScreen,\n                resultAnswerCorrect = correctAnswer.index == index,\n                answerCorrect = selectedAnswer == correctAnswer,\n                onClick = { onOptionClick(SelectedAnswer.fromIndex(index)) }\n            )\n        }\n    }\n}\n\n@Composable\n@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)\ninternal fun CardQuestionAnswer(\n    modifier: Modifier = Modifier,\n    answer: String,\n    selected: Boolean,\n    isResults: Boolean = false,\n    resultAnswerCorrect: Boolean = false,\n    answerCorrect: Boolean = false,\n    colors: CardQuestionAnswerColors = CardQuestionAnswerDefaults.cardColors(),\n    tonalElevation: Dp = CardQuestionAnswerDefaults.cardTonalElevation,\n    textPadding: Dp = CardQuestionAnswerDefaults.textPadding,\n    textStyle: TextStyle = CardQuestionAnswerDefaults.textStyle,\n    cardShape: Shape = CardQuestionAnswerDefaults.cardShape,\n    onClick: () -> Unit\n) {\n    val containerColor by colors.containerColor(\n        isResults = isResults,\n        selected = selected,\n        answerCorrect = answerCorrect,\n        resultAnswerCorrect = resultAnswerCorrect\n    )\n    val containerColorAnimated by animateColorAsState(\n        targetValue = containerColor,\n        label = \"container color animation\"\n    )\n\n    val contentColor by colors.contentColor(\n        isResults = isResults,\n        selected = selected,\n        answerCorrect = answerCorrect,\n        resultAnswerCorrect = resultAnswerCorrect\n    )\n    val contentColorAnimated by animateColorAsState(\n        targetValue = contentColor,\n        label = \"content color animation\"\n    )\n\n    Surface(\n        modifier = modifier.fillMaxWidth(),\n        shape = cardShape,\n        tonalElevation = tonalElevation,\n        color = containerColorAnimated,\n        onClick = onClick,\n        selected = selected,\n        enabled = !isResults\n    ) {\n        Text(\n            text = answer,\n            modifier = Modifier.padding(textPadding),\n            style = textStyle,\n            color = contentColorAnimated\n        )\n    }\n}\n\nobject CardQuestionAnswerDefaults {\n    val cardTonalElevation = 8.dp\n\n    val textPadding: Dp @Composable get() = MaterialTheme.spacing.medium\n\n    val textStyle: TextStyle @Composable get() = MaterialTheme.typography.bodyLarge\n\n    val cardShape = CircleShape\n\n    @Composable\n    fun cardColors(\n        normalContainerColor: Color = MaterialTheme.colorScheme.surface,\n        normalContentColor: Color = MaterialTheme.colorScheme.onSurface,\n        selectedContainerColor: Color = MaterialTheme.colorScheme.primary,\n        selectedContentColor: Color = MaterialTheme.colorScheme.onPrimary,\n        correctContainerColor: Color = MaterialTheme.extendedColors.getColorByKey(key = CustomColor.Key.Green),\n        correctContentColor: Color = MaterialTheme.extendedColors.getOnColorByKey(key = CustomColor.Key.Green),\n        incorrectContainerColor: Color = MaterialTheme.extendedColors.getColorByKey(key = CustomColor.Key.Red),\n        incorrectContentColor: Color = MaterialTheme.extendedColors.getOnColorByKey(key = CustomColor.Key.Red)\n    ): CardQuestionAnswerColors = CardQuestionAnswerColors(\n        normalContainerColor = normalContainerColor,\n        normalContentColor = normalContentColor,\n        selectedContainerColor = selectedContainerColor,\n        selectedContentColor = selectedContentColor,\n        correctContainerColor = correctContainerColor,\n        correctContentColor = correctContentColor,\n        incorrectContainerColor = incorrectContainerColor,\n        incorrectContentColor = incorrectContentColor\n    )\n}\n\n@Immutable\nclass CardQuestionAnswerColors internal constructor(\n    private val normalContainerColor: Color,\n    private val normalContentColor: Color,\n    private val selectedContainerColor: Color,\n    private val selectedContentColor: Color,\n    private val correctContainerColor: Color,\n    private val correctContentColor: Color,\n    private val incorrectContainerColor: Color,\n    private val incorrectContentColor: Color,\n) {\n    @Composable\n    internal fun containerColor(\n        isResults: Boolean,\n        selected: Boolean,\n        answerCorrect: Boolean,\n        resultAnswerCorrect: Boolean\n    ): State<Color> {\n        return rememberUpdatedState(\n            newValue = when {\n                isResults && selected && !answerCorrect -> incorrectContainerColor\n                isResults && resultAnswerCorrect -> correctContainerColor\n                selected -> selectedContainerColor\n                else -> normalContainerColor\n            }\n        )\n    }\n\n    @Composable\n    internal fun contentColor(\n        isResults: Boolean,\n        selected: Boolean,\n        answerCorrect: Boolean,\n        resultAnswerCorrect: Boolean\n    ): State<Color> {\n        return rememberUpdatedState(\n            newValue = when {\n                isResults && selected && !answerCorrect -> incorrectContentColor\n                isResults && resultAnswerCorrect -> correctContentColor\n                selected -> selectedContentColor\n                else -> normalContentColor\n            }\n        )\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (other == null || other !is CardQuestionAnswerColors) return false\n\n        if (normalContainerColor != other.normalContainerColor) return false\n        if (normalContentColor != other.normalContentColor) return false\n\n        if (incorrectContainerColor != other.incorrectContainerColor) return false\n        if (incorrectContentColor != other.incorrectContentColor) return false\n\n        if (correctContainerColor != other.correctContainerColor) return false\n        if (correctContentColor != other.correctContentColor) return false\n\n        if (selectedContainerColor != other.selectedContainerColor) return false\n        if (selectedContentColor != other.selectedContentColor) return false\n\n        return true\n    }\n\n    override fun hashCode(): Int {\n        var result = normalContainerColor.hashCode()\n        result = 31 * result + normalContentColor.hashCode()\n        result = 31 * result + incorrectContainerColor.hashCode()\n        result = 31 * result + incorrectContentColor.hashCode()\n        result = 31 * result + correctContainerColor.hashCode()\n        result = 31 * result + correctContentColor.hashCode()\n        result = 31 * result + selectedContainerColor.hashCode()\n        result = 31 * result + selectedContentColor.hashCode()\n        return result\n    }\n}\n\n\n@Composable\n@PreviewLightDark\nprivate fun CardQuestionsPreview() {\n    NewQuizTheme {\n        Surface {\n            CardQuestionAnswers(\n                answers = listOf(\"A\", \"B\", \"C\", \"D\"),\n                selectedAnswer = SelectedAnswer.fromIndex(1),\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun CardQuestionOptionPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) selected: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            CardQuestionAnswer(\n                answer = \"Answer A\",\n                selected = selected,\n                onClick = {},\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/components/MultiChoiceQuizContainer.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.consumeWindowInsets\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.ExtendedFloatingActionButton\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.windowsizeclass.WindowHeightSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowWidthSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.constraintlayout.compose.ConstraintLayout\nimport androidx.constraintlayout.compose.Dimension\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.util.plus\n\n@Composable\ninternal fun MultiChoiceQuizContainer(\n    modifier: Modifier = Modifier,\n    loading: Boolean = false,\n    windowSizeClass: WindowSizeClass,\n    answerSelected: Boolean,\n    topBarContent: @Composable () -> Unit,\n    stepsContent: @Composable BoxScope.() -> Unit,\n    questionPositionContent: @Composable () -> Unit,\n    questionDescriptionContent: @Composable () -> Unit,\n    answersContent: @Composable BoxScope.() -> Unit,\n    questionImageContent: (@Composable () -> Unit)? = null,\n    onVerifyQuestionClick: () -> Unit\n) {\n    val verticalContent = windowSizeClass.heightSizeClass > WindowHeightSizeClass.Compact\n\n    val horizontalFractionSize = remember {\n        if (windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded) 0.5f else 1f\n    }\n\n    Scaffold(\n        modifier = modifier,\n        topBar = topBarContent,\n        floatingActionButton = {\n            if (answerSelected) {\n                ExtendedFloatingActionButton(\n                    onClick = onVerifyQuestionClick,\n                    text = {\n                        Text(text = stringResource(id = R.string.verify))\n                    },\n                    icon = {\n                        Icon(\n                            imageVector = Icons.Rounded.Check,\n                            contentDescription = stringResource(id = R.string.verify)\n                        )\n                    }\n                )\n            }\n        }\n    ) { innerPadding ->\n        if (loading) {\n            Column(\n                modifier = Modifier\n                    .fillMaxSize()\n                    .padding(innerPadding),\n                horizontalAlignment = Alignment.CenterHorizontally,\n                verticalArrangement = Arrangement.Center\n            ) {\n                CircularProgressIndicator()\n                Spacer(modifier = Modifier.height(MaterialTheme.spacing.medium))\n                Text(text = stringResource(id = R.string.loading_questions))\n            }\n        } else {\n            if (verticalContent) {\n                VerticalContent(\n                    innerPadding = innerPadding,\n                    stepsContent = stepsContent,\n                    questionPositionContent = questionPositionContent,\n                    questionDescriptionContent = questionDescriptionContent,\n                    answersContent = answersContent,\n                    questionImageContent = questionImageContent,\n                    horizontalFractionSize = horizontalFractionSize\n                )\n            } else {\n                HorizontalContent(\n                    innerPadding = innerPadding,\n                    stepsContent = stepsContent,\n                    questionPositionContent = questionPositionContent,\n                    questionDescriptionContent = questionDescriptionContent,\n                    answersContent = answersContent,\n                    questionImageContent = questionImageContent,\n                    windowSizeClass = windowSizeClass\n                )\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun VerticalContent(\n    modifier: Modifier = Modifier,\n    innerPadding: PaddingValues = PaddingValues(),\n    horizontalFractionSize: Float = 1f,\n    stepsContent: @Composable BoxScope.() -> Unit,\n    questionPositionContent: @Composable () -> Unit,\n    questionDescriptionContent: @Composable () -> Unit,\n    answersContent: @Composable BoxScope.() -> Unit,\n    questionImageContent: (@Composable () -> Unit)? = null,\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    LazyColumn(\n        contentPadding = innerPadding + PaddingValues(\n            start = spaceMedium,\n            end = spaceMedium,\n            top = spaceMedium,\n            bottom = MaterialTheme.spacing.extraLarge\n        ),\n        verticalArrangement = Arrangement.spacedBy(spaceMedium),\n        modifier = modifier\n            .consumeWindowInsets(innerPadding)\n            .fillMaxWidth(horizontalFractionSize)\n    ) {\n        item(key = \"steps\") {\n            Box(\n                content = stepsContent,\n                modifier = Modifier\n                    .fillParentMaxWidth()\n                    .padding(vertical = spaceMedium)\n            )\n        }\n\n        item { questionPositionContent() }\n        item { questionDescriptionContent() }\n\n        // Question image, if exists\n        if (questionImageContent != null) {\n            item {\n                questionImageContent()\n            }\n        }\n\n        item {\n            Box(\n                content = answersContent,\n                modifier = Modifier.fillParentMaxWidth()\n            )\n        }\n    }\n}\n\n@Composable\nprivate fun HorizontalContent(\n    modifier: Modifier = Modifier,\n    innerPadding: PaddingValues = PaddingValues(),\n    windowSizeClass: WindowSizeClass,\n    stepsContent: @Composable BoxScope.() -> Unit,\n    questionPositionContent: @Composable () -> Unit,\n    questionDescriptionContent: @Composable () -> Unit,\n    answersContent: @Composable BoxScope.() -> Unit,\n    questionImageContent: (@Composable () -> Unit)? = null,\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n    val spaceLarge = MaterialTheme.spacing.large\n\n    val heightCompact = windowSizeClass.heightSizeClass == WindowHeightSizeClass.Compact\n\n    ConstraintLayout(\n        modifier = modifier\n            .padding(innerPadding + PaddingValues(spaceMedium))\n            .consumeWindowInsets(innerPadding)\n            .fillMaxSize()\n    ) {\n        val (stepsRef, descriptionRef, answersRef) = createRefs()\n\n        // Steps content\n        Box(\n            modifier = Modifier.constrainAs(stepsRef) {\n                width = Dimension.wrapContent\n\n                if (heightCompact) {\n                    top.linkTo(parent.top)\n                    start.linkTo(descriptionRef.start)\n                    end.linkTo(descriptionRef.end)\n                } else {\n                    top.linkTo(parent.top, spaceLarge)\n                    start.linkTo(parent.start)\n                    end.linkTo(parent.end)\n                }\n            },\n            content = stepsContent\n        )\n\n        // Position, description and image content\n        Column(\n            modifier = Modifier.constrainAs(descriptionRef) {\n                if (heightCompact) {\n                    top.linkTo(stepsRef.bottom, spaceLarge)\n                    start.linkTo(parent.start)\n                    end.linkTo(answersRef.start)\n                } else {\n                    top.linkTo(stepsRef.bottom, spaceLarge)\n                    start.linkTo(parent.start)\n                    end.linkTo(parent.end)\n                }\n\n                width = Dimension.fillToConstraints\n            }\n        ) {\n            questionPositionContent()\n            Spacer(modifier = Modifier.height(spaceMedium))\n            questionDescriptionContent()\n            // Question image, if exists\n            if (questionImageContent != null) {\n                Spacer(modifier = Modifier.height(spaceMedium))\n                questionImageContent()\n            }\n        }\n\n        // Answers content\n        Box(\n            modifier = Modifier.constrainAs(answersRef) {\n                if (heightCompact) {\n                    top.linkTo(parent.top)\n                    start.linkTo(descriptionRef.end)\n                    end.linkTo(parent.end)\n                } else {\n                    top.linkTo(descriptionRef.bottom, spaceMedium)\n                    start.linkTo(parent.start)\n                    end.linkTo(parent.end)\n                }\n\n                width = Dimension.fillToConstraints\n            },\n            content = answersContent\n        )\n    }\n}\n"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/components/QuizStepView.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components\n\nimport androidx.compose.animation.AnimatedVisibility\nimport androidx.compose.animation.animateColorAsState\nimport androidx.compose.animation.scaleIn\nimport androidx.compose.animation.scaleOut\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.lazy.LazyRow\nimport androidx.compose.foundation.lazy.itemsIndexed\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material.icons.rounded.Close\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun QuizStepViewRow(\n    modifier: Modifier = Modifier,\n    questionSteps: List<MultiChoiceQuestionStep>,\n    isResultsScreen: Boolean = false,\n    onClick: (index: Int, questionStep: MultiChoiceQuestionStep) -> Unit = { _, _ -> }\n) {\n    val rowDescription = stringResource(id = CoreR.string.quiz_steps_row_container)\n\n    LazyRow(\n        modifier = modifier\n            .fillMaxWidth()\n            .semantics {\n                contentDescription = rowDescription\n            },\n        verticalAlignment = Alignment.CenterVertically,\n        horizontalArrangement = Arrangement.spacedBy(6.dp, Alignment.CenterHorizontally),\n        contentPadding = PaddingValues(horizontal = MaterialTheme.spacing.medium)\n    ) {\n        itemsIndexed(\n            items = questionSteps,\n            key = { _, step -> step.question.id }\n        ) { index, step ->\n            val position = index + 1\n\n            QuizStepView(\n                questionStep = step,\n                position = position,\n                enabled = isResultsScreen,\n                onClick = { onClick(index, step) }\n            )\n        }\n    }\n}\n\n@Composable\ninternal fun QuizStepView(\n    modifier: Modifier = Modifier,\n    questionStep: MultiChoiceQuestionStep,\n    position: Int,\n    enabled: Boolean = true,\n    onClick: () -> Unit = {}\n) {\n    val stepBackgroundColor by animateColorAsState(\n        targetValue =  if (questionStep is MultiChoiceQuestionStep.Current) {\n            MaterialTheme.colorScheme.tertiary\n        } else {\n            MaterialTheme.colorScheme.surface\n        },\n        label = \"Step background color\"\n    )\n\n    val stepTextColor by animateColorAsState(\n        targetValue = if (questionStep is MultiChoiceQuestionStep.Current) {\n            MaterialTheme.colorScheme.onTertiary\n        } else {\n            MaterialTheme.colorScheme.onSurface\n        },\n        label = \"Step text color\"\n    )\n\n    QuizStepView(\n        modifier = modifier,\n        questionStep = questionStep,\n        position = position,\n        stepBackgroundColor = stepBackgroundColor,\n        stepTextColor = stepTextColor,\n        enabled = enabled,\n        onClick = onClick\n    )\n}\n\n@Composable\ninternal fun QuizStepView(\n    modifier: Modifier = Modifier,\n    questionStep: MultiChoiceQuestionStep,\n    position: Int,\n    stepBackgroundColor: Color,\n    stepTextColor: Color,\n    enabled: Boolean,\n    onClick: () -> Unit\n) {\n    Surface(\n        shape = CircleShape,\n        tonalElevation = 8.dp,\n        modifier = modifier.size(35.dp),\n        color = stepBackgroundColor,\n        onClick = onClick,\n        enabled = enabled\n    ) {\n        Box(\n            contentAlignment = Alignment.Center,\n            modifier = Modifier.fillMaxSize()\n        ) {\n            AnimatedVisibility(\n                visible = questionStep is MultiChoiceQuestionStep.Completed,\n                enter = scaleIn(),\n                exit = scaleOut()\n            ) {\n                val correct = questionStep is MultiChoiceQuestionStep.Completed && questionStep.correct\n\n                Icon(\n                    imageVector = if (correct) Icons.Rounded.Check else Icons.Rounded.Close,\n                    contentDescription = if (correct) {\n                        stringResource(id = CoreR.string.question_n_correct, position)\n                    } else {\n                        stringResource(id = CoreR.string.question_n_incorrect, position)\n                    },\n                    modifier = Modifier.size(25.dp),\n                    tint = stepTextColor\n                )\n            }\n\n            val textStyle = if (position >= 100) {\n                MaterialTheme.typography.bodyMedium\n            } else MaterialTheme.typography.titleLarge\n\n            AnimatedVisibility(\n                visible = questionStep !is MultiChoiceQuestionStep.Completed,\n                enter = scaleIn(),\n                exit = scaleOut()\n            ) {\n                Text(\n                    text = position.toString(),\n                    color = stepTextColor,\n                    style = textStyle,\n                    textAlign = TextAlign.Center\n                )\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun AllStepsPreview() {\n    val items = listOf(\n        MultiChoiceQuestionStep.Completed(\n            question = getBasicMultiChoiceQuestion(),\n            correct = true\n        ),\n        MultiChoiceQuestionStep.Completed(\n            question = getBasicMultiChoiceQuestion(),\n            correct = false\n        ),\n        MultiChoiceQuestionStep.Current(question = getBasicMultiChoiceQuestion()),\n        MultiChoiceQuestionStep.NotCurrent(question = getBasicMultiChoiceQuestion()),\n    )\n\n    NewQuizTheme(dynamicColor = false) {\n        Surface {\n            LazyRow(\n                horizontalArrangement = Arrangement.spacedBy(6.dp),\n                modifier = Modifier.padding(16.dp)\n            ) {\n                itemsIndexed(\n                    items = items,\n                    key = { index, _ -> index }\n                ) { _, step ->\n                    QuizStepView(\n                        questionStep = step,\n                        position = (1..9).random(),\n                        onClick = {}\n                    )\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/components/QuizTopBar.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.MoreVert\nimport androidx.compose.material.icons.rounded.Save\nimport androidx.compose.material.icons.rounded.SkipNext\nimport androidx.compose.material3.DropdownMenu\nimport androidx.compose.material3.DropdownMenuItem\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.windowsizeclass.WindowHeightSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport androidx.constraintlayout.compose.ConstraintLayout\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.animationsEnabled\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.RemainingTimeComponent\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.model.RemainingTime\nimport com.infinitepower.newquiz.multi_choice_quiz.MULTI_CHOICE_QUIZ_COUNTDOWN_TIME\nimport kotlin.time.Duration.Companion.seconds\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun QuizTopBar(\n    modifier: Modifier = Modifier,\n    windowHeightSizeClass: WindowHeightSizeClass,\n    remainingTime: RemainingTime,\n    skipsAvailable: Boolean,\n    questionSaved: Boolean,\n    currentQuestionNull: Boolean = true,\n    onBackClick: () -> Unit,\n    onSkipClick: () -> Unit,\n    onSaveClick: () -> Unit\n) {\n    val progressIndicatorVisible = windowHeightSizeClass > WindowHeightSizeClass.Compact\n\n    QuizTopBarContainer(\n        modifier = modifier,\n        backButtonContent = { BackIconButton(onClick = onBackClick) },\n        remainingTimerContent = {\n            if (!remainingTime.isZero()) {\n                RemainingTimeComponent(\n                    remainingTime = remainingTime,\n                    maxTime = MULTI_CHOICE_QUIZ_COUNTDOWN_TIME,\n                    showProgressIndicator = progressIndicatorVisible,\n                    animationsEnabled = MaterialTheme.animationsEnabled.multiChoice\n                )\n            }\n        },\n        onSkipClick = onSkipClick,\n        onSaveClick = onSaveClick,\n        skipsAvailable = skipsAvailable,\n        questionSaved = questionSaved,\n        currentQuestionNull = currentQuestionNull\n    )\n}\n\n@Composable\nprivate fun QuizTopBarContainer(\n    modifier: Modifier = Modifier,\n    skipsAvailable: Boolean,\n    questionSaved: Boolean,\n    currentQuestionNull: Boolean = true,\n    backButtonContent: @Composable BoxScope.() -> Unit,\n    remainingTimerContent: @Composable BoxScope.() -> Unit,\n    onSkipClick: () -> Unit,\n    onSaveClick: () -> Unit\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    var moreOptionsPopupExpanded by remember { mutableStateOf(false) }\n\n    ConstraintLayout(modifier = modifier) {\n        val (btnBackRef, progressRef, btnMoreOptions) = createRefs()\n\n        Box(\n            modifier = Modifier.constrainAs(btnBackRef) {\n                top.linkTo(progressRef.top)\n                bottom.linkTo(progressRef.bottom)\n                start.linkTo(parent.start, spaceMedium)\n            },\n            content = backButtonContent,\n            contentAlignment = Alignment.Center\n        )\n\n        Box(\n            modifier = Modifier.constrainAs(progressRef) {\n                top.linkTo(parent.top, spaceMedium)\n                start.linkTo(parent.start)\n                end.linkTo(parent.end)\n            },\n            content = remainingTimerContent,\n            contentAlignment = Alignment.Center\n        )\n\n        if (!currentQuestionNull && (skipsAvailable || !questionSaved)) {\n            Box(\n                modifier = Modifier.constrainAs(btnMoreOptions) {\n                    top.linkTo(progressRef.top)\n                    bottom.linkTo(progressRef.bottom)\n                    end.linkTo(parent.end, spaceMedium)\n                },\n                contentAlignment = Alignment.Center\n            ) {\n                IconButton(onClick = { moreOptionsPopupExpanded = true }) {\n                    Icon(\n                        imageVector = Icons.Rounded.MoreVert,\n                        contentDescription = stringResource(CoreR.string.more_options)\n                    )\n                }\n\n                DropdownMenu(\n                    expanded = moreOptionsPopupExpanded,\n                    onDismissRequest = { moreOptionsPopupExpanded = false }\n                ) {\n                    if (skipsAvailable) {\n                        DropdownMenuItem(\n                            text = { Text(text = stringResource(CoreR.string.skip)) },\n                            leadingIcon = {\n                                Icon(\n                                    imageVector = Icons.Rounded.SkipNext,\n                                    contentDescription = stringResource(CoreR.string.skip)\n                                )\n                            },\n                            onClick = onSkipClick\n                        )\n                    }\n                    if (!questionSaved) {\n                        DropdownMenuItem(\n                            text = { Text(text = stringResource(CoreR.string.save)) },\n                            leadingIcon = {\n                                Icon(\n                                    imageVector = Icons.Rounded.Save,\n                                    contentDescription = stringResource(CoreR.string.save)\n                                )\n                            },\n                            onClick = onSaveClick\n                        )\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun QuizTopBarPreview() {\n    NewQuizTheme {\n        Surface {\n            QuizTopBar(\n                windowHeightSizeClass = WindowHeightSizeClass.Medium,\n                remainingTime = RemainingTime(20.seconds),\n                skipsAvailable = true,\n                onBackClick = {},\n                onSkipClick = {},\n                onSaveClick = {},\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .padding(16.dp),\n                currentQuestionNull = false,\n                questionSaved = false\n            )\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/components/difficulty/BaseCardDifficultyContent.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components.difficulty\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.SignalCellularAlt\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.spacing\n\n@Composable\ninternal fun BaseCardDifficultyContent(\n    modifier: Modifier = Modifier,\n    text: String\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    Column(modifier = modifier.padding(spaceMedium)) {\n        Text(\n            text = text,\n            style = MaterialTheme.typography.titleMedium\n        )\n        Spacer(modifier = Modifier.height(spaceMedium))\n        Box(modifier = Modifier.padding(start = MaterialTheme.spacing.extraLarge)) {\n            Icon(\n                imageVector = Icons.Rounded.SignalCellularAlt,\n                contentDescription = text,\n                modifier = Modifier.size(35.dp)\n            )\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/components/difficulty/FilledCardDifficulty.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components.difficulty\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Card\nimport androidx.compose.material3.CardDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.CustomColor\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.extendedColors\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.core.util.model.getText\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\n\n@Composable\ninternal fun FilledCardDifficulty(\n    modifier: Modifier = Modifier,\n    multiChoiceQuizDifficulty: QuestionDifficulty,\n    enabled: Boolean = true,\n    onClick: () -> Unit\n) {\n    val colorRoles = MaterialTheme.extendedColors.getColorsByKey(\n        key = when (multiChoiceQuizDifficulty) {\n            is QuestionDifficulty.Easy -> CustomColor.Key.Green\n            is QuestionDifficulty.Medium -> CustomColor.Key.Yellow\n            is QuestionDifficulty.Hard -> CustomColor.Key.Red\n        }\n    )\n\n    FilledCardDifficulty(\n        modifier = modifier,\n        text = multiChoiceQuizDifficulty.getText().asString(),\n        containerColor = colorRoles.color,\n        contentColor = colorRoles.onColor,\n        onClick = onClick,\n        enabled = enabled\n    )\n}\n\n@Composable\ninternal fun FilledCardDifficulty(\n    modifier: Modifier = Modifier,\n    text: String,\n    containerColor: Color,\n    contentColor: Color,\n    enabled: Boolean = true,\n    onClick: () -> Unit\n) {\n    FilledCardDifficultyContainer(\n        modifier = modifier,\n        containerColor = containerColor,\n        contentColor = contentColor,\n        onClick = onClick,\n        enabled = enabled\n    ) {\n        BaseCardDifficultyContent(text = text)\n    }\n}\n\n@Composable\nprivate fun FilledCardDifficultyContainer(\n    modifier: Modifier = Modifier,\n    containerColor: Color,\n    contentColor: Color,\n    enabled: Boolean = true,\n    onClick: () -> Unit,\n    content: @Composable () -> Unit\n) {\n    Card(\n        modifier = modifier,\n        colors = CardDefaults.cardColors(\n            containerColor = containerColor,\n            contentColor = contentColor\n        ),\n        enabled = enabled,\n        onClick = onClick,\n        shape = MaterialTheme.shapes.large\n    ) {\n        content()\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun CardDifficultyPreview() {\n    NewQuizTheme {\n        Surface {\n            Column(\n                modifier = Modifier.padding(16.dp),\n                verticalArrangement = Arrangement.spacedBy(16.dp)\n            ) {\n                FilledCardDifficulty(\n                    multiChoiceQuizDifficulty = QuestionDifficulty.Easy,\n                    onClick = {}\n                )\n                FilledCardDifficulty(\n                    multiChoiceQuizDifficulty = QuestionDifficulty.Medium,\n                    onClick = {}\n                )\n                FilledCardDifficulty(\n                    multiChoiceQuizDifficulty = QuestionDifficulty.Hard,\n                    onClick = {}\n                )\n            }\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/components/difficulty/OutlinedCardDifficulty.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components.difficulty\n\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.CardDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedCard\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.CustomColor\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.extendedColors\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.core.util.model.getText\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\n\n@Composable\ninternal fun OutlinedCardDifficulty(\n    modifier: Modifier = Modifier,\n    multiChoiceQuizDifficulty: QuestionDifficulty,\n    onClick: () -> Unit\n) {\n    val contentColor = MaterialTheme.extendedColors.getColorByKey(\n        key = when (multiChoiceQuizDifficulty) {\n            is QuestionDifficulty.Easy -> CustomColor.Key.Green\n            is QuestionDifficulty.Medium -> CustomColor.Key.Yellow\n            is QuestionDifficulty.Hard -> CustomColor.Key.Red\n        }\n    )\n\n    OutlinedCardDifficulty(\n        modifier = modifier,\n        text = multiChoiceQuizDifficulty.getText().asString(),\n        color = contentColor,\n        onClick = onClick\n    )\n}\n\n@Composable\ninternal fun OutlinedCardDifficulty(\n    modifier: Modifier = Modifier,\n    text: String,\n    color: Color,\n    onClick: () -> Unit\n) {\n    OutlinedCardDifficultyContainer(\n        modifier = modifier,\n        color = color,\n        onClick = onClick,\n    ) {\n        BaseCardDifficultyContent(text = text)\n    }\n}\n\n@Composable\nprivate fun OutlinedCardDifficultyContainer(\n    modifier: Modifier = Modifier,\n    color: Color,\n    onClick: () -> Unit,\n    content: @Composable () -> Unit\n) {\n    OutlinedCard(\n        modifier = modifier,\n        colors = CardDefaults.outlinedCardColors(\n            contentColor = color\n        ),\n        onClick = onClick,\n        border = BorderStroke(1.dp, color),\n        shape = MaterialTheme.shapes.large\n    ) {\n        content()\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun OutlinedCardDifficultyPreview() {\n    NewQuizTheme {\n        Surface {\n            Column(\n                modifier = Modifier.padding(16.dp),\n                verticalArrangement = Arrangement.spacedBy(16.dp)\n            ) {\n                OutlinedCardDifficulty(\n                    multiChoiceQuizDifficulty = QuestionDifficulty.Easy,\n                    onClick = {}\n                )\n                OutlinedCardDifficulty(\n                    multiChoiceQuizDifficulty = QuestionDifficulty.Medium,\n                    onClick = {}\n                )\n                OutlinedCardDifficulty(\n                    multiChoiceQuizDifficulty = QuestionDifficulty.Hard,\n                    onClick = {}\n                )\n            }\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/components/difficulty/SelectableDifficultyRow.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.components.difficulty\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyRow\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\ninternal fun SelectableDifficultyRow(\n    modifier: Modifier = Modifier,\n    selectedDifficulty: QuestionDifficulty?,\n    setSelectedDifficulty: (QuestionDifficulty?) -> Unit\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val items = remember { QuestionDifficulty.items() }\n\n    LazyRow(\n        modifier = modifier,\n        horizontalArrangement = Arrangement.spacedBy(spaceMedium),\n        verticalAlignment = Alignment.CenterVertically,\n        contentPadding = PaddingValues(horizontal = spaceMedium)\n    ) {\n        item {\n            if (selectedDifficulty == null) {\n                FilledCardDifficulty(\n                    text = stringResource(id = CoreR.string.random),\n                    containerColor = MaterialTheme.colorScheme.primaryContainer,\n                    contentColor = MaterialTheme.colorScheme.onPrimaryContainer,\n                    onClick = {}\n                )\n            } else {\n                OutlinedCardDifficulty(\n                    text = stringResource(id = CoreR.string.random),\n                    color = MaterialTheme.colorScheme.primary,\n                    onClick = { setSelectedDifficulty(null) }\n                )\n            }\n        }\n\n        items(\n            items = items,\n            key = { it.id }\n        ) { item ->\n            if (item == selectedDifficulty) {\n                FilledCardDifficulty(\n                    multiChoiceQuizDifficulty = item,\n                    onClick = {}\n                )\n            } else {\n                OutlinedCardDifficulty(\n                    multiChoiceQuizDifficulty = item,\n                    onClick = { setSelectedDifficulty(item) }\n                )\n            }\n        }\n    }\n}\n\n\n@Composable\n@PreviewLightDark\nprivate fun CardDifficultyPreview() {\n    val (selectedItem, setSelectedItem) = remember {\n        // When null, difficulty will be random\n        mutableStateOf<QuestionDifficulty?>(QuestionDifficulty.Easy)\n    }\n\n    NewQuizTheme {\n        Surface {\n            SelectableDifficultyRow(\n                modifier = Modifier\n                    .padding(16.dp)\n                    .fillMaxWidth(),\n                selectedDifficulty = selectedItem,\n                setSelectedDifficulty = setSelectedItem\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/list/MultiChoiceQuizListScreen.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.list\n\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Save\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.pluralStringResource\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.LocalAnalyticsHelper\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.home.HomeLazyColumn\nimport com.infinitepower.newquiz.core.ui.home.homeCategoriesItems\nimport com.infinitepower.newquiz.core.ui.home_card.components.HomeGroupTitle\nimport com.infinitepower.newquiz.core.ui.home_card.components.HomeMediumCard\nimport com.infinitepower.newquiz.core.ui.home_card.components.PlayRandomQuizCard\nimport com.infinitepower.newquiz.core.ui.home_card.model.CardIcon\nimport com.infinitepower.newquiz.core.ui.home_card.model.HomeCardItem\nimport com.infinitepower.newquiz.model.multi_choice_quiz.toBaseCategory\nimport com.infinitepower.newquiz.model.question.QuestionDifficulty\nimport com.infinitepower.newquiz.multi_choice_quiz.components.difficulty.SelectableDifficultyRow\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.SavedMultiChoiceQuestionsScreenDestination\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@Destination\nfun MultiChoiceQuizListScreen(\n    navigator: DestinationsNavigator,\n    viewModel: MultiChoiceQuizListScreenViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    MultiChoiceQuizListScreenImpl(\n        uiState = uiState,\n        navigator = navigator\n    )\n}\n\n@Composable\nprivate fun MultiChoiceQuizListScreenImpl(\n    uiState: MultiChoiceQuizListScreenUiState,\n    navigator: DestinationsNavigator\n) {\n    val analyticsHelper = LocalAnalyticsHelper.current\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val questionsAvailableText = pluralStringResource(\n        id = CoreR.plurals.n_questions_available,\n        count = uiState.savedQuestionsSize,\n        formatArgs = arrayOf(uiState.savedQuestionsSize)\n    )\n\n    val (selectedDifficulty, setSelectedDifficulty) = remember {\n        // When null, difficulty will be random\n        mutableStateOf<QuestionDifficulty?>(null)\n    }\n\n    var seeAllCategories by remember { mutableStateOf(false) }\n\n    HomeLazyColumn(\n        contentPadding = PaddingValues(\n            top = MaterialTheme.spacing.medium,\n            bottom = MaterialTheme.spacing.large\n        )\n    ) {\n        item {\n            PlayRandomQuizCard(\n                modifier = Modifier\n                    .fillParentMaxWidth()\n                    .padding(horizontal = spaceMedium),\n                title = stringResource(id = CoreR.string.quiz_with_random_categories),\n                buttonTitle = stringResource(id = CoreR.string.random_quiz),\n                containerMainColor = MaterialTheme.colorScheme.primary,\n                onClick = {\n                    navigator.navigate(MultiChoiceQuizScreenDestination(difficulty = selectedDifficulty?.id))\n                },\n                enabled = uiState.internetConnectionAvailable,\n            )\n        }\n\n        item {\n            Text(\n                text = stringResource(id = CoreR.string.difficulty),\n                style = MaterialTheme.typography.bodyMedium,\n                modifier = Modifier.padding(horizontal = spaceMedium)\n            )\n        }\n\n        item {\n            SelectableDifficultyRow(\n                selectedDifficulty = selectedDifficulty,\n                setSelectedDifficulty = setSelectedDifficulty\n            )\n        }\n\n        item {\n            Text(\n                text = stringResource(id = CoreR.string.categories),\n                style = MaterialTheme.typography.bodyMedium,\n                modifier = Modifier.padding(horizontal = spaceMedium)\n            )\n        }\n\n        homeCategoriesItems(\n            contentPadding = PaddingValues(\n                horizontal = spaceMedium\n            ),\n            seeAllCategories = seeAllCategories,\n            recentCategories = uiState.homeCategories.recentCategories,\n            otherCategories = uiState.homeCategories.otherCategories,\n            isInternetAvailable = uiState.internetConnectionAvailable,\n            showConnectionInfo = uiState.showCategoryConnectionInfo,\n            onCategoryClick = { category ->\n                analyticsHelper.logEvent(\n                    AnalyticsEvent.CategoryClicked(\n                        game = AnalyticsEvent.Game.MULTI_CHOICE_QUIZ,\n                        categoryId = category.id,\n                        otherData = mapOf(\n                            \"difficulty\" to selectedDifficulty?.id\n                        )\n                    )\n                )\n\n                navigator.navigate(\n                    MultiChoiceQuizScreenDestination(\n                        category = category.toBaseCategory(),\n                        difficulty = selectedDifficulty?.id\n                    )\n                )\n            },\n            onSeeAllCategoriesClick = { seeAllCategories = !seeAllCategories }\n        )\n\n        item {\n            HomeGroupTitle(\n                title = stringResource(id = CoreR.string.saved_questions),\n                modifier = Modifier.padding(horizontal = spaceMedium)\n            )\n        }\n\n        item {\n            HomeMediumCard(\n                modifier = Modifier\n                    .fillParentMaxWidth()\n                    .padding(horizontal = spaceMedium),\n                data = HomeCardItem.MediumCard(\n                    title = CoreR.string.saved_questions,\n                    icon = CardIcon.Icon(Icons.Rounded.Save),\n                    onClick = { navigator.navigate(SavedMultiChoiceQuestionsScreenDestination) },\n                    description = questionsAvailableText\n                )\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun MultiChoiceCategoriesPreview() {\n    NewQuizTheme {\n        Surface {\n            MultiChoiceQuizListScreenImpl(\n                uiState = MultiChoiceQuizListScreenUiState(),\n                navigator = EmptyDestinationsNavigator\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/list/MultiChoiceQuizListScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.list\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.domain.repository.home.HomeCategories\nimport com.infinitepower.newquiz.domain.repository.home.emptyHomeCategories\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceCategory\n\n@Keep\ndata class MultiChoiceQuizListScreenUiState(\n    val savedQuestionsSize: Int = 0,\n    val homeCategories: HomeCategories<MultiChoiceCategory> = emptyHomeCategories(),\n    val internetConnectionAvailable: Boolean = true,\n    val showCategoryConnectionInfo: ShowCategoryConnectionInfo = ShowCategoryConnectionInfo.NONE\n)"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/list/MultiChoiceQuizListScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.list\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.core.network.NetworkStatusTracker\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.StateFlow\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.stateIn\nimport javax.inject.Inject\n\n@HiltViewModel\nclass MultiChoiceQuizListScreenViewModel @Inject constructor(\n    savedQuestionsRepository: SavedMultiChoiceQuestionsRepository,\n    networkStatusTracker: NetworkStatusTracker,\n    recentCategoriesRepository: RecentCategoriesRepository\n) : ViewModel() {\n    // Searching if this is the problem with the TooManyRequestsException\n    /*\n    val uiState: StateFlow<MultiChoiceQuizListScreenUiState> = combine(\n        savedQuestionsRepository.getFlowQuestions(),\n        networkStatusTracker.isOnline\n    ) { savedQuestions, isOnline ->\n        MultiChoiceQuizListScreenUiState(\n            savedQuestionsSize = savedQuestions.size,\n            internetConnectionAvailable = isOnline\n        )\n    }.catch {e -> // Try to fix ConnectivityManager$TooManyRequestsException\n        e.printStackTrace()\n        emit(MultiChoiceQuizListScreenUiState())\n    }.flatMapLatest { state ->\n        // Get the recent categories\n        recentCategoriesRepository\n            .getMultiChoiceCategories(isInternetAvailable = state.internetConnectionAvailable)\n            .map { homeCategories ->\n                state.copy(homeCategories = homeCategories)\n            }\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(5_000),\n        initialValue = MultiChoiceQuizListScreenUiState()\n    )\n\n     */\n\n    val uiState: StateFlow<MultiChoiceQuizListScreenUiState> = combine(\n        savedQuestionsRepository.getCount(),\n        recentCategoriesRepository.getMultiChoiceCategories(\n            isInternetAvailable = networkStatusTracker.isCurrentlyConnected()\n        ),\n        recentCategoriesRepository.getShowCategoryConnectionInfoFlow()\n    ) { savedQuestionsCount, recentCategories, showCategoryConnectionInfo ->\n        MultiChoiceQuizListScreenUiState(\n            savedQuestionsSize = savedQuestionsCount,\n            homeCategories = recentCategories,\n            internetConnectionAvailable = networkStatusTracker.isCurrentlyConnected(),\n            showCategoryConnectionInfo = showCategoryConnectionInfo\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(5_000),\n        initialValue = MultiChoiceQuizListScreenUiState()\n    )\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/results/MultiChoiceQuizResultsScreen.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.results\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.aspectRatio\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedButton\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.WindowHeightSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalConfiguration\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.DpSize\nimport androidx.compose.ui.unit.dp\nimport coil.compose.AsyncImage\nimport com.airbnb.lottie.compose.LottieAnimation\nimport com.airbnb.lottie.compose.LottieCompositionSpec\nimport com.airbnb.lottie.compose.LottieConstants\nimport com.airbnb.lottie.compose.rememberLottieComposition\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestionStep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.SelectedAnswer\nimport com.infinitepower.newquiz.model.multi_choice_quiz.countCorrectQuestions\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport com.infinitepower.newquiz.multi_choice_quiz.components.CardQuestionAnswers\nimport com.infinitepower.newquiz.multi_choice_quiz.components.QuizStepViewRow\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizResultsScreenDestination\nimport com.infinitepower.newquiz.multi_choice_quiz.destinations.MultiChoiceQuizScreenDestination\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.serialization.json.Json\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@Destination\nfun MultiChoiceQuizResultsScreen(\n    questionStepsStr: String,\n    category: MultiChoiceBaseCategory,\n    byInitialQuestions: Boolean = false,\n    difficulty: String? = null,\n    navigator: DestinationsNavigator,\n    windowSizeClass: WindowSizeClass\n) {\n    val questionSteps: List<MultiChoiceQuestionStep.Completed> = remember {\n        Json.decodeFromString(questionStepsStr)\n    }\n\n    val initialQuestions = remember(questionSteps) {\n        ArrayList(questionSteps.map(MultiChoiceQuestionStep::question))\n    }\n\n    MultiChoiceQuizResultsScreenImpl(\n        questionSteps = questionSteps,\n        onBackClick = navigator::popBackStack,\n        onPlayAgainClick = {\n            navigator.navigate(\n                MultiChoiceQuizScreenDestination(\n                    initialQuestions = if (byInitialQuestions) initialQuestions else ArrayList(),\n                    category = category,\n                    difficulty = difficulty\n                )\n            ) {\n                popUpTo(MultiChoiceQuizResultsScreenDestination) {\n                    inclusive = true\n                }\n\n                launchSingleTop = true\n            }\n        },\n        windowHeightSizeClass = windowSizeClass.heightSizeClass\n    )\n}\n\n@Composable\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun MultiChoiceQuizResultsScreenImpl(\n    questionSteps: List<MultiChoiceQuestionStep.Completed>,\n    windowHeightSizeClass: WindowHeightSizeClass,\n    onBackClick: () -> Unit,\n    onPlayAgainClick: () -> Unit\n) {\n    val winnerSpec = LottieCompositionSpec.RawRes(R.raw.trophy_winner)\n    val winnerLottieComposition by rememberLottieComposition(spec = winnerSpec)\n\n    val spaceMedium = MaterialTheme.spacing.medium\n    val spaceLarge = MaterialTheme.spacing.large\n\n    val (questionDialog, setQuestionDialog) = remember {\n        mutableStateOf<Int?>(null)\n    }\n\n    Scaffold(\n        topBar = {\n            TopAppBar(\n                title = {\n                    Text(text = stringResource(id = CoreR.string.results_screen))\n                }\n            )\n        }\n    ) { innerPadding ->\n        ResultsScreenContainer(\n            modifier = Modifier\n                .padding(innerPadding)\n                .padding(spaceMedium)\n                .fillMaxSize(),\n            windowHeightSizeClass = windowHeightSizeClass,\n            animationContent = {\n                Surface(\n                    modifier = Modifier.size(300.dp),\n                    tonalElevation = 8.dp,\n                    shape = CircleShape\n                ) {\n                    LottieAnimation(\n                        composition = winnerLottieComposition,\n                        modifier = Modifier\n                            .fillMaxSize()\n                            .padding(spaceMedium),\n                        iterations = LottieConstants.IterateForever,\n                    )\n                }\n            },\n            resultScoreTextContent = {\n                Text(\n                    text = stringResource(\n                        id = CoreR.string.n_correct_questions,\n                        \"${questionSteps.countCorrectQuestions()}/${questionSteps.size}\"\n                    ),\n                    style = MaterialTheme.typography.headlineMedium\n                )\n            },\n            stepRowContent = {\n                QuizStepViewRow(\n                    modifier = Modifier.fillMaxWidth(),\n                    questionSteps = questionSteps,\n                    isResultsScreen = true,\n                    onClick = { index, _ ->\n                        setQuestionDialog(index)\n                    }\n                )\n            },\n            actionButtonsContent = {\n                Row(\n                    verticalAlignment = Alignment.CenterVertically,\n                    horizontalArrangement = Arrangement.spacedBy(spaceMedium),\n                    modifier = Modifier.padding(bottom = spaceLarge)\n                ) {\n                    OutlinedButton(\n                        onClick = onPlayAgainClick,\n                        modifier = Modifier.weight(1f)\n                    ) {\n                        Text(text = stringResource(id = CoreR.string.play_again))\n                    }\n                    Button(\n                        onClick = onBackClick,\n                        modifier = Modifier.weight(1f)\n                    ) {\n                        Text(text = stringResource(id = CoreR.string.back))\n                    }\n                }\n            }\n        )\n    }\n\n    if (questionDialog != null) {\n        val questionStep = questionSteps[questionDialog]\n        val question = questionStep.question\n\n        AlertDialog(\n            onDismissRequest = { setQuestionDialog(null) },\n            title = { Text(text = question.description) },\n            text = {\n                LazyColumn {\n                    // Question image, if exists\n                    question.image?.let { imageUrl ->\n                        item {\n                            Spacer(modifier = Modifier.height(spaceMedium))\n                            AsyncImage(\n                                model = imageUrl,\n                                contentDescription = \"Flag Image\",\n                                modifier = Modifier\n                                    .aspectRatio(16 / 9f)\n                                    .clip(MaterialTheme.shapes.medium),\n                                contentScale = ContentScale.Crop\n                            )\n                            Spacer(modifier = Modifier.height(spaceMedium))\n                        }\n                    }\n\n                    item {\n                        CardQuestionAnswers(\n                            answers = question.answers,\n                            selectedAnswer = questionStep.selectedAnswer,\n                            correctAnswer = SelectedAnswer.fromIndex(question.correctAns),\n                            isResultsScreen = true\n                        )\n                    }\n                }\n            },\n            confirmButton = {\n                TextButton(onClick = { setQuestionDialog(null) }) {\n                    Text(text = stringResource(id = CoreR.string.close))\n                }\n            }\n        )\n    }\n}\n\n@Composable\nprivate fun ResultsScreenContainer(\n    modifier: Modifier = Modifier,\n    windowHeightSizeClass: WindowHeightSizeClass,\n    animationContent: @Composable BoxScope.() -> Unit,\n    resultScoreTextContent: @Composable () -> Unit,\n    stepRowContent: @Composable () -> Unit,\n    actionButtonsContent: @Composable () -> Unit\n) {\n    val spaceLarge = MaterialTheme.spacing.large\n\n    if (windowHeightSizeClass == WindowHeightSizeClass.Compact) {\n        Row(\n            modifier = modifier,\n            verticalAlignment = Alignment.CenterVertically\n        ) {\n            Box(\n                content = animationContent,\n                modifier = Modifier.weight(1f)\n            )\n            Column(\n                modifier = Modifier.weight(1f),\n                horizontalAlignment = Alignment.CenterHorizontally\n            ) {\n                resultScoreTextContent()\n                Spacer(modifier = Modifier.height(spaceLarge))\n                stepRowContent()\n                Spacer(modifier = Modifier.weight(1f))\n                actionButtonsContent()\n            }\n        }\n    } else {\n        Column(\n            modifier = modifier,\n            horizontalAlignment = Alignment.CenterHorizontally\n        ) {\n            Box(content = animationContent)\n            Spacer(modifier = Modifier.height(spaceLarge))\n            resultScoreTextContent()\n            Spacer(modifier = Modifier.height(spaceLarge))\n            stepRowContent()\n            Spacer(modifier = Modifier.weight(1f))\n            actionButtonsContent()\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)\nprivate fun MultiChoiceQuizResultsScreenPreview() {\n    val questionSteps = listOf(\n        MultiChoiceQuestionStep.Completed(\n            question = getBasicMultiChoiceQuestion(),\n            correct = true\n        ),\n        MultiChoiceQuestionStep.Completed(\n            question = getBasicMultiChoiceQuestion(),\n            correct = false\n        ),\n        MultiChoiceQuestionStep.Completed(\n            question = getBasicMultiChoiceQuestion(),\n            correct = true\n        ),\n    )\n\n    val configuration = LocalConfiguration.current\n    val screenHeight = configuration.screenHeightDp.dp\n    val screenWidth = configuration.screenWidthDp.dp\n    val windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(screenWidth, screenHeight))\n\n    NewQuizTheme {\n        Surface {\n            MultiChoiceQuizResultsScreenImpl(\n                questionSteps = questionSteps,\n                onBackClick = {},\n                onPlayAgainClick = {},\n                windowHeightSizeClass = windowSizeClass.heightSizeClass\n            )\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/saved_questions/SavedMultiChoiceQuestionsScreen.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.saved_questions\n\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.automirrored.rounded.Sort\nimport androidx.compose.material.icons.rounded.Category\nimport androidx.compose.material.icons.rounded.Close\nimport androidx.compose.material.icons.rounded.Delete\nimport androidx.compose.material.icons.rounded.Description\nimport androidx.compose.material.icons.rounded.Download\nimport androidx.compose.material.icons.rounded.MoreVert\nimport androidx.compose.material.icons.rounded.PlayArrow\nimport androidx.compose.material.icons.rounded.Reorder\nimport androidx.compose.material.icons.rounded.SaveAlt\nimport androidx.compose.material.icons.rounded.SelectAll\nimport androidx.compose.material.icons.rounded.Shuffle\nimport androidx.compose.material3.BottomAppBar\nimport androidx.compose.material3.BottomAppBarDefaults\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.DropdownMenu\nimport androidx.compose.material3.DropdownMenuItem\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.ExtendedFloatingActionButton\nimport androidx.compose.material3.FloatingActionButtonDefaults\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.LinearProgressIndicator\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.material3.TopAppBarDefaults\nimport androidx.compose.material3.TopAppBarScrollBehavior\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.LocalAnalyticsHelper\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.RoundedPolygonShape\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.saved.SortSavedQuestionsBy\nimport com.infinitepower.newquiz.multi_choice_quiz.saved_questions.components.SavedQuestionItem\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@Destination\n@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)\nfun SavedMultiChoiceQuestionsScreen(\n    navigator: DestinationsNavigator,\n    savedQuestionsScreenNavigator: SavedQuestionsScreenNavigator,\n    viewModel: SavedMultiChoiceQuestionsViewModel = hiltViewModel()\n) {\n    val uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n    val analyticsHelper = LocalAnalyticsHelper.current\n\n    SavedMultiChoiceQuestionsScreenImpl(\n        uiState = uiState,\n        onBackClick = navigator::popBackStack,\n        onEvent = viewModel::onEvent,\n        playWithQuestions = { questions ->\n            analyticsHelper.logEvent(AnalyticsEvent.MultiChoicePlaySavedQuestions(questions.size))\n            savedQuestionsScreenNavigator.navigateToMultiChoiceQuiz(ArrayList(questions))\n        }\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\n@ExperimentalFoundationApi\nprivate fun SavedMultiChoiceQuestionsScreenImpl(\n    uiState: SavedMultiChoiceQuestionsUiState,\n    onBackClick: () -> Unit,\n    playWithQuestions: (questions: List<MultiChoiceQuestion>) -> Unit,\n    onEvent: (event: SavedMultiChoiceQuestionsUiEvent) -> Unit\n) {\n    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()\n\n    val (moreVertPopupExpanded, setMoreVertPopupExpanded) = remember {\n        mutableStateOf(false)\n    }\n\n    val (sortPopupExpanded, setSortPopupExpanded) = remember {\n        mutableStateOf(false)\n    }\n\n    Scaffold(\n        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),\n        topBar = {\n            SavedQuestionsTopAppBar(\n                scrollBehavior = scrollBehavior,\n                selectedQuestionsSize = uiState.selectedQuestions.size,\n                onBackClick = onBackClick,\n                onCleanSelectedClick = {\n                    onEvent(SavedMultiChoiceQuestionsUiEvent.SelectNone)\n                }\n            )\n        },\n        bottomBar = {\n            if (uiState.questions.isNotEmpty()) {\n                BottomAppBar(\n                    actions = {\n                        TopBarActionButton(\n                            imageVector = Icons.Rounded.MoreVert,\n                            contentDescription = stringResource(id = CoreR.string.more_options),\n                            onClick = { setMoreVertPopupExpanded(true) }\n                        )\n                        MoreVertPopup(\n                            expanded = moreVertPopupExpanded,\n                            isQuestionsNotEmpty = uiState.questions.isNotEmpty(),\n                            questionsSelected = uiState.selectedQuestions.isNotEmpty(),\n                            onDismiss = { setMoreVertPopupExpanded(false) },\n                            onSelectAllClick = {\n                                onEvent(SavedMultiChoiceQuestionsUiEvent.SelectAll)\n                                setMoreVertPopupExpanded(false)\n                            },\n                            onDeleteAllSelectedClick = {\n                                onEvent(SavedMultiChoiceQuestionsUiEvent.DeleteAllSelected)\n                                setMoreVertPopupExpanded(false)\n                            },\n                            onDownloadQuestionsClick = {\n                                onEvent(SavedMultiChoiceQuestionsUiEvent.DownloadQuestions)\n                                setMoreVertPopupExpanded(false)\n                            }\n                        )\n                        TopBarActionButton(\n                            imageVector = Icons.AutoMirrored.Rounded.Sort,\n                            contentDescription = stringResource(id = CoreR.string.sort_questions),\n                            onClick = { setSortPopupExpanded(true) }\n                        )\n                        SortPopup(\n                            expanded = sortPopupExpanded,\n                            onDismiss = { setSortPopupExpanded(false) },\n                            onSortClick = { sortBy ->\n                                setSortPopupExpanded(false)\n                                onEvent(SavedMultiChoiceQuestionsUiEvent.SortQuestions(sortBy))\n                            }\n                        )\n                        TopBarActionButton(\n                            imageVector = Icons.Rounded.Shuffle,\n                            contentDescription = stringResource(id = CoreR.string.play_with_random_saved_questions),\n                            onClick = { playWithQuestions(uiState.randomQuestions()) }\n                        )\n                    },\n                    floatingActionButton = {\n                        if (uiState.selectedQuestions.isNotEmpty()) {\n                            ExtendedFloatingActionButton(\n                                text = { Text(text = stringResource(id = CoreR.string.play)) },\n                                icon = {\n                                    Icon(\n                                        imageVector = Icons.Rounded.PlayArrow,\n                                        contentDescription = stringResource(id = CoreR.string.play_with_selected_questions)\n                                    )\n                                },\n                                onClick = {\n                                    // Ensure question size is below 50\n                                    val playQuestions = uiState.selectedQuestions.take(50)\n                                    playWithQuestions(playQuestions)\n                                },\n                                containerColor = BottomAppBarDefaults.bottomAppBarFabColor,\n                                elevation = FloatingActionButtonDefaults.bottomAppBarFabElevation()\n                            )\n                        }\n                    }\n                )\n            }\n        }\n    ) { innerPadding ->\n        Column(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(innerPadding),\n            horizontalAlignment = Alignment.CenterHorizontally\n        ) {\n            if (uiState.loading || uiState.downloadingQuestions) {\n                LinearProgressIndicator(modifier = Modifier.fillMaxWidth())\n            }\n\n            if (!uiState.loading) {\n                if (uiState.questions.isEmpty()) {\n                    EmptyQuestions(\n                        modifier = Modifier.padding(innerPadding),\n                        onDownloadQuestionsClick = {\n                            onEvent(SavedMultiChoiceQuestionsUiEvent.DownloadQuestions)\n                        },\n                        downloadButtonEnabled = !uiState.downloadingQuestions\n                    )\n                } else {\n                    LazyColumn {\n                        items(\n                            items = uiState.questions,\n                            key = { it.id }\n                        ) { question ->\n                            val selected = question in uiState.selectedQuestions\n\n                            SavedQuestionItem(\n                                modifier = Modifier\n                                    .fillMaxWidth()\n                                    .animateItem(),\n                                question = question,\n                                selected = selected,\n                                onClick = {\n                                    onEvent(SavedMultiChoiceQuestionsUiEvent.SelectQuestion(question))\n                                }\n                            )\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\n@ExperimentalMaterial3Api\nprivate fun SavedQuestionsTopAppBar(\n    modifier: Modifier = Modifier,\n    scrollBehavior: TopAppBarScrollBehavior,\n    selectedQuestionsSize: Int,\n    onBackClick: () -> Unit,\n    onCleanSelectedClick: () -> Unit\n) {\n    val title = if (selectedQuestionsSize == 0) {\n        stringResource(id = CoreR.string.saved_questions)\n    } else {\n        selectedQuestionsSize.toString()\n    }\n\n    val barColors = if (selectedQuestionsSize == 0) {\n        TopAppBarDefaults.topAppBarColors()\n    } else {\n        TopAppBarDefaults.topAppBarColors(\n//            containerColor = MaterialTheme.colorScheme.primary,\n//            titleContentColor = MaterialTheme.colorScheme.onPrimary,\n//            navigationIconContentColor = MaterialTheme.colorScheme.onPrimary\n        )\n    }\n\n    TopAppBar(\n        modifier = modifier,\n        title = { Text(text = title) },\n        scrollBehavior = scrollBehavior,\n        navigationIcon = {\n            if (selectedQuestionsSize == 0) {\n                BackIconButton(onClick = onBackClick)\n            } else {\n                IconButton(onClick = onCleanSelectedClick) {\n                    Icon(\n                        imageVector = Icons.Rounded.Close,\n                        contentDescription = null\n                    )\n                }\n            }\n        },\n        colors = barColors\n    )\n}\n\n@Composable\nprivate fun EmptyQuestions(\n    modifier: Modifier = Modifier,\n    onDownloadQuestionsClick: () -> Unit,\n    downloadButtonEnabled: Boolean = true\n) {\n    Column(\n        modifier = modifier\n            .fillMaxSize()\n            .padding(MaterialTheme.spacing.large),\n        horizontalAlignment = Alignment.CenterHorizontally,\n        verticalArrangement = Arrangement.spacedBy(\n            space = MaterialTheme.spacing.medium,\n            alignment = Alignment.CenterVertically\n        )\n    ) {\n        Surface(\n            tonalElevation = 4.dp,\n            shape = RoundedPolygonShape(sides = 8)\n        ) {\n            Icon(\n                modifier = Modifier\n                    .size(170.dp)\n                    .padding(48.dp),\n                imageVector = Icons.Rounded.SaveAlt,\n                contentDescription = null,\n            )\n        }\n        Text(\n            text = stringResource(id = CoreR.string.no_questions_saved_yet),\n            style = MaterialTheme.typography.titleLarge\n        )\n        Text(\n            text = stringResource(id = CoreR.string.no_questions_saved_description),\n            style = MaterialTheme.typography.bodyLarge,\n            color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),\n            textAlign = TextAlign.Center\n        )\n        Button(\n            onClick = onDownloadQuestionsClick,\n            enabled = downloadButtonEnabled\n        ) {\n            Text(text = stringResource(id = CoreR.string.download_questions))\n        }\n    }\n}\n\n@Composable\nprivate fun TopBarActionButton(\n    imageVector: ImageVector,\n    contentDescription: String,\n    onClick: () -> Unit\n) {\n    IconButton(onClick = onClick) {\n        Icon(\n            imageVector = imageVector,\n            contentDescription = contentDescription\n        )\n    }\n}\n\n@Composable\nprivate fun MoreVertPopup(\n    isQuestionsNotEmpty: Boolean,\n    questionsSelected: Boolean,\n    expanded: Boolean,\n    onDismiss: () -> Unit,\n    onSelectAllClick: () -> Unit,\n    onDeleteAllSelectedClick: () -> Unit,\n    onDownloadQuestionsClick: () -> Unit\n) {\n    DropdownMenu(\n        expanded = expanded,\n        onDismissRequest = onDismiss\n    ) {\n        if (isQuestionsNotEmpty) {\n            if (questionsSelected) {\n                DropdownMenuItem(\n                    text = { Text(text = stringResource(id = CoreR.string.unselect_all)) },\n                    leadingIcon = {\n                        Icon(\n                            imageVector = Icons.Rounded.SelectAll,\n                            contentDescription = stringResource(id = CoreR.string.unselect_all)\n                        )\n                    },\n                    onClick = onSelectAllClick\n                )\n                DropdownMenuItem(\n                    text = {\n                        Text(text = stringResource(id = CoreR.string.delete_selected))\n                    },\n                    leadingIcon = {\n                        Icon(\n                            imageVector = Icons.Rounded.Delete,\n                            contentDescription = stringResource(id = CoreR.string.delete_selected)\n                        )\n                    },\n                    onClick = onDeleteAllSelectedClick\n                )\n            } else {\n                DropdownMenuItem(\n                    text = { Text(text = stringResource(id = CoreR.string.select_all)) },\n                    leadingIcon = {\n                        Icon(\n                            imageVector = Icons.Rounded.SelectAll,\n                            contentDescription = stringResource(id = CoreR.string.select_all)\n                        )\n                    },\n                    onClick = onSelectAllClick\n                )\n            }\n        }\n        DropdownMenuItem(\n            text = { Text(text = stringResource(id = CoreR.string.download_questions)) },\n            leadingIcon = {\n                Icon(\n                    imageVector = Icons.Rounded.Download,\n                    contentDescription = stringResource(id = CoreR.string.download_questions)\n                )\n            },\n            onClick = onDownloadQuestionsClick\n        )\n    }\n}\n\n@Composable\nprivate fun SortPopup(\n    expanded: Boolean,\n    onDismiss: () -> Unit,\n    onSortClick: (sortBy: SortSavedQuestionsBy) -> Unit,\n) {\n    DropdownMenu(\n        expanded = expanded,\n        onDismissRequest = onDismiss\n    ) {\n        DropdownMenuItem(\n            text = { Text(text = stringResource(id = CoreR.string.sort_by_default)) },\n            leadingIcon = {\n                Icon(\n                    imageVector = Icons.Rounded.Reorder,\n                    contentDescription = stringResource(id = CoreR.string.sort_by_default)\n                )\n            },\n            onClick = { onSortClick(SortSavedQuestionsBy.BY_DEFAULT) }\n        )\n        DropdownMenuItem(\n            text = { Text(text = stringResource(id = CoreR.string.sort_by_description)) },\n            leadingIcon = {\n                Icon(\n                    imageVector = Icons.Rounded.Description,\n                    contentDescription = stringResource(id = CoreR.string.sort_by_description)\n                )\n            },\n            onClick = { onSortClick(SortSavedQuestionsBy.BY_DESCRIPTION) }\n        )\n        DropdownMenuItem(\n            text = { Text(text = stringResource(id = CoreR.string.sort_by_category)) },\n            leadingIcon = {\n                Icon(\n                    imageVector = Icons.Rounded.Category,\n                    contentDescription = stringResource(id = CoreR.string.sort_by_category)\n                )\n            },\n            onClick = { onSortClick(SortSavedQuestionsBy.BY_CATEGORY) }\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)\nprivate fun SavedMultiChoiceQuestionsScreenPreview() {\n    val questions = List(10) {\n        getBasicMultiChoiceQuestion()\n    }\n\n    NewQuizTheme {\n        SavedMultiChoiceQuestionsScreenImpl(\n            uiState = SavedMultiChoiceQuestionsUiState(\n                questions = questions,\n                selectedQuestions = questions.take(3),\n                loading = false\n            ),\n            onBackClick = {},\n            onEvent = {},\n            playWithQuestions = {}\n        )\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/saved_questions/SavedMultiChoiceQuestionsScreenNavigator.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.saved_questions\n\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\n\ninterface SavedQuestionsScreenNavigator {\n    fun navigateToMultiChoiceQuiz(initialQuestions: ArrayList<MultiChoiceQuestion>)\n}\n\ninternal object SavedQuestionsScreenNavigatorPreview : SavedQuestionsScreenNavigator {\n    override fun navigateToMultiChoiceQuiz(initialQuestions: ArrayList<MultiChoiceQuestion>) {\n        println(\"Navigating to quick quiz\")\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/saved_questions/SavedMultiChoiceQuestionsUiEvent.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.saved_questions\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.saved.SortSavedQuestionsBy\n\nsealed interface SavedMultiChoiceQuestionsUiEvent {\n    data class SelectQuestion(val question: MultiChoiceQuestion) : SavedMultiChoiceQuestionsUiEvent\n\n    data object SelectAll : SavedMultiChoiceQuestionsUiEvent\n\n    data object SelectNone : SavedMultiChoiceQuestionsUiEvent\n\n    data object DeleteAllSelected : SavedMultiChoiceQuestionsUiEvent\n\n    data object DownloadQuestions : SavedMultiChoiceQuestionsUiEvent\n\n    @Keep\n    data class SortQuestions(\n        val sortBy: SortSavedQuestionsBy\n    ) : SavedMultiChoiceQuestionsUiEvent\n}\n"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/saved_questions/SavedMultiChoiceQuestionsUiState.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.saved_questions\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\n\n@Keep\ndata class SavedMultiChoiceQuestionsUiState(\n    val questions: List<MultiChoiceQuestion> = emptyList(),\n    val selectedQuestions: List<MultiChoiceQuestion> = emptyList(),\n    val loading: Boolean = true,\n    val downloadingQuestions: Boolean = false\n) {\n    fun randomQuestions(limit: Int = 5): List<MultiChoiceQuestion> {\n        return questions.shuffled().take(limit)\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/saved_questions/SavedMultiChoiceQuestionsViewModel.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.saved_questions\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport androidx.work.Constraints\nimport androidx.work.NetworkType\nimport androidx.work.OneTimeWorkRequestBuilder\nimport androidx.work.WorkInfo\nimport androidx.work.WorkManager\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.ui.SnackbarController\nimport com.infinitepower.newquiz.data.worker.multichoicequiz.DownloadMultiChoiceQuestionsWorker\nimport com.infinitepower.newquiz.domain.repository.multi_choice_quiz.saved_questions.SavedMultiChoiceQuestionsRepository\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.saved.SortSavedQuestionsBy\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.first\nimport kotlinx.coroutines.flow.flatMapLatest\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\n\n@HiltViewModel\n@OptIn(ExperimentalCoroutinesApi::class)\nclass SavedMultiChoiceQuestionsViewModel @Inject constructor(\n    private val savedQuestionsRepository: SavedMultiChoiceQuestionsRepository,\n    private val workManager: WorkManager,\n    private val analyticsHelper: AnalyticsHelper\n) : ViewModel() {\n    private val _uiState = MutableStateFlow(SavedMultiChoiceQuestionsUiState())\n    val uiState = _uiState.asStateFlow()\n\n    private val sortQuestionsBy = MutableStateFlow(SortSavedQuestionsBy.BY_DEFAULT)\n\n    init {\n        sortQuestionsBy\n            .flatMapLatest { sortBy ->\n                savedQuestionsRepository.getFlowQuestions(sortBy)\n            }.onEach { questions ->\n                _uiState.update { currentState ->\n                    currentState.copy(\n                        questions = questions,\n                        loading = false\n                    )\n                }\n            }.launchIn(viewModelScope)\n    }\n\n    fun onEvent(event: SavedMultiChoiceQuestionsUiEvent) {\n        when (event) {\n            is SavedMultiChoiceQuestionsUiEvent.SelectQuestion -> selectQuestion(event.question)\n            is SavedMultiChoiceQuestionsUiEvent.SelectAll -> selectAllQuestions()\n            is SavedMultiChoiceQuestionsUiEvent.SelectNone -> _uiState.update {\n                it.copy(selectedQuestions = emptyList())\n            }\n\n            is SavedMultiChoiceQuestionsUiEvent.DeleteAllSelected -> deleteAllSelected()\n            is SavedMultiChoiceQuestionsUiEvent.DownloadQuestions -> downloadQuestions()\n            is SavedMultiChoiceQuestionsUiEvent.SortQuestions -> {\n                viewModelScope.launch(Dispatchers.IO) {\n                    sortQuestionsBy.emit(event.sortBy)\n                }\n            }\n        }\n    }\n\n    private fun selectQuestion(question: MultiChoiceQuestion) {\n        _uiState.update { currentState ->\n            val selectedQuestions = if (question in currentState.selectedQuestions) {\n                currentState.selectedQuestions - question\n            } else {\n                currentState.selectedQuestions + question\n            }\n\n            currentState.copy(selectedQuestions = selectedQuestions)\n        }\n    }\n\n    private fun selectAllQuestions() {\n        _uiState.update { currentState ->\n            val selectedQuestions = if (currentState.selectedQuestions.isEmpty()) {\n                currentState.questions\n            } else emptyList()\n\n            currentState.copy(selectedQuestions = selectedQuestions)\n        }\n    }\n\n    private fun downloadQuestions() {\n        analyticsHelper.logEvent(AnalyticsEvent.MultiChoiceDownloadQuestions)\n\n        val downloadQuestionsRequest =\n            OneTimeWorkRequestBuilder<DownloadMultiChoiceQuestionsWorker>()\n                .setConstraints(\n                    Constraints(\n                        requiredNetworkType = NetworkType.CONNECTED\n                    )\n                ).build()\n\n        workManager.enqueue(downloadQuestionsRequest)\n\n        workManager\n            .getWorkInfoByIdFlow(downloadQuestionsRequest.id)\n            .onEach { info ->\n                if (info?.state == WorkInfo.State.SUCCEEDED) {\n                    SnackbarController.sendShortMessage(\"Downloaded successfully\")\n                } else if (info?.state == WorkInfo.State.FAILED) {\n                    SnackbarController.sendShortMessage(\"Failed to download the questions\")\n                }\n\n                _uiState.update { currentState ->\n                    val isFinished = info?.state?.isFinished ?: false\n                    currentState.copy(downloadingQuestions = !isFinished)\n                }\n            }.launchIn(viewModelScope)\n    }\n\n    private fun deleteAllSelected() = viewModelScope.launch(Dispatchers.IO) {\n        val allSelectedQuestions = uiState.first().selectedQuestions\n        savedQuestionsRepository.deleteAllSelected(allSelectedQuestions)\n        // Clear the selected questions after deleting them\n        _uiState.update {\n            it.copy(selectedQuestions = emptyList())\n        }\n    }\n}"
  },
  {
    "path": "multi-choice-quiz/src/main/java/com/infinitepower/newquiz/multi_choice_quiz/saved_questions/components/SavedQuestionItem.kt",
    "content": "package com.infinitepower.newquiz.multi_choice_quiz.saved_questions.components\n\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Android\nimport androidx.compose.material.icons.rounded.Category\nimport androidx.compose.material.icons.rounded.Flag\nimport androidx.compose.material.icons.rounded.FormatListNumbered\nimport androidx.compose.material.icons.rounded.Numbers\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.ReadOnlyComposable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceBaseCategory\nimport com.infinitepower.newquiz.model.multi_choice_quiz.MultiChoiceQuestion\nimport com.infinitepower.newquiz.model.multi_choice_quiz.getBasicMultiChoiceQuestion\n\n@Composable\n@ExperimentalMaterial3Api\ninternal fun SavedQuestionItem(\n    modifier: Modifier = Modifier,\n    question: MultiChoiceQuestion,\n    selected: Boolean,\n    onClick: () -> Unit,\n) {\n    Surface(\n        onClick = onClick,\n        selected = selected,\n        tonalElevation = if (selected) 8.dp else 0.dp,\n        modifier = modifier\n    ) {\n        Row(\n            verticalAlignment = Alignment.CenterVertically,\n            modifier = Modifier.padding(MaterialTheme.spacing.medium)\n        ) {\n            Icon(\n                imageVector = getIconByCategory(multiChoiceQuestionCategory = question.category),\n                contentDescription = null\n            )\n            Spacer(modifier = Modifier.width(MaterialTheme.spacing.medium))\n            Text(\n                text = question.description,\n                style = MaterialTheme.typography.bodyLarge,\n                maxLines = 1,\n                overflow = TextOverflow.Ellipsis,\n            )\n        }\n    }\n}\n\n@Composable\n@ReadOnlyComposable\nprivate fun getIconByCategory(\n    multiChoiceQuestionCategory: MultiChoiceBaseCategory\n) = when (multiChoiceQuestionCategory) {\n    is MultiChoiceBaseCategory.Logo -> Icons.Rounded.Android\n    is MultiChoiceBaseCategory.Flag, MultiChoiceBaseCategory.CountryCapitalFlags -> Icons.Rounded.Flag\n    is MultiChoiceBaseCategory.GuessMathSolution -> Icons.Rounded.FormatListNumbered\n    is MultiChoiceBaseCategory.NumberTrivia -> Icons.Rounded.Numbers\n    else -> Icons.Rounded.Category\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun SavedMultiChoiceQuestionsScreenPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) selected: Boolean\n) {\n    NewQuizTheme {\n        Surface {\n            SavedQuestionItem(\n                question = getBasicMultiChoiceQuestion(),\n                onClick = {},\n                selected = selected,\n                modifier = Modifier.padding(16.dp)\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "multi-choice-quiz/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>"
  },
  {
    "path": "settings.gradle.kts",
    "content": "pluginManagement {\n    includeBuild(\"build-logic\")\n\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n        maven { url = uri(\"https://jitpack.io\") }\n    }\n}\n\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n\n\n    repositories {\n        google()\n        mavenCentral()\n        maven { url = uri(\"https://jitpack.io\") }\n    }\n}\n\nrootProject.name = \"NewQuiz\"\n\nenableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\n\ninclude(\":app\")\n\ninclude(\":core\")\ninclude(\":core:analytics\")\ninclude(\":core:database\")\ninclude(\":core:datastore\")\ninclude(\":core:remote-config\")\ninclude(\":core:testing\")\ninclude(\":core:translation\")\ninclude(\":core:user-services\")\n\ninclude(\":feature:daily-challenge\")\ninclude(\":feature:maze\")\ninclude(\":feature:settings\")\ninclude(\":feature:profile\")\n\ninclude(\":model\")\ninclude(\":domain\")\ninclude(\":data\")\ninclude(\":multi-choice-quiz\")\ninclude(\":wordle\")\ninclude(\":comparison-quiz\")\n"
  },
  {
    "path": "wordle/.gitignore",
    "content": "/build"
  },
  {
    "path": "wordle/README.md",
    "content": "# Wordle\n\nThis is the code for the wordle game mode.\n\n![NewQuiz purple light](../pictures/wordle.jpg)"
  },
  {
    "path": "wordle/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.newquiz.android.library.compose)\n    alias(libs.plugins.newquiz.android.hilt)\n    alias(libs.plugins.newquiz.android.compose.destinations)\n}\n\nandroid {\n    namespace = \"com.infinitepower.newquiz.wordle\"\n}\n\ndependencies {\n    implementation(libs.androidx.core.ktx)\n\n    implementation(libs.androidx.lifecycle.livedata.ktx)\n    implementation(libs.androidx.lifecycle.runtimeCompose)\n\n    implementation(libs.androidx.compose.ui.tooling)\n    implementation(libs.androidx.compose.ui.tooling.preview)\n    implementation(libs.androidx.compose.material3)\n    implementation(libs.androidx.compose.material3.windowSizeClass)\n    implementation(libs.androidx.compose.material.iconsExtended)\n    debugImplementation(libs.androidx.compose.ui.testManifest)\n    implementation(libs.androidx.constraintlayout.compose)\n\n    androidTestImplementation(libs.androidx.compose.ui.test)\n    androidTestImplementation(libs.androidx.test.runner)\n    androidTestImplementation(libs.androidx.test.rules)\n\n    // Hilt navigation compose utils\n    implementation(libs.hilt.navigationCompose)\n    // Hilt work manager\n    implementation(libs.hilt.ext.work)\n    ksp(libs.hilt.ext.compiler)\n\n    implementation(libs.kotlinx.datetime)\n\n    implementation(libs.androidx.work.ktx)\n    androidTestImplementation(libs.androidx.work.testing)\n\n    implementation(projects.core)\n    implementation(projects.core.analytics)\n    implementation(projects.core.userServices)\n    implementation(projects.model)\n    implementation(projects.domain)\n    implementation(projects.data)\n    androidTestImplementation(projects.core.testing)\n}\n"
  },
  {
    "path": "wordle/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "wordle/proguard-rules.pro",
    "content": "-dontwarn java.lang.invoke.StringConcatFactory"
  },
  {
    "path": "wordle/src/androidTest/java/com/infinitepower/newquiz/wordle/WordleScreenTest.kt",
    "content": "package com.infinitepower.newquiz.wordle\n\nimport android.util.Log\nimport androidx.activity.ComponentActivity\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.calculateWindowSizeClass\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.ui.platform.LocalInspectionMode\nimport androidx.compose.ui.test.*\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.hilt.work.HiltWorkerFactory\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.work.WorkManager\nimport androidx.work.testing.SynchronousExecutor\nimport androidx.work.testing.WorkManagerTestInitHelper\nimport com.infinitepower.newquiz.core.analytics.LocalDebugAnalyticsHelper\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport com.infinitepower.newquiz.domain.repository.wordle.WordleRepository\nimport com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator\nimport dagger.hilt.android.testing.HiltAndroidRule\nimport dagger.hilt.android.testing.HiltAndroidTest\nimport org.junit.Rule\nimport org.junit.runner.RunWith\nimport java.util.*\nimport javax.inject.Inject\nimport kotlin.test.BeforeTest\nimport kotlin.test.Test\n\n@HiltAndroidTest\n@RunWith(AndroidJUnit4::class)\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)\nclass WordleScreenTest {\n    @get:Rule(order = 0)\n    val hiltRule = HiltAndroidRule(this)\n\n    @get:Rule(order = 1)\n    val composeRule = createAndroidComposeRule<ComponentActivity>()\n\n    private lateinit var savedStateHandle: SavedStateHandle\n    private lateinit var viewModel: WordleScreenViewModel\n\n    @Inject\n    lateinit var wordleRepository: WordleRepository\n\n\n    @Inject\n    lateinit var workerFactory: HiltWorkerFactory\n\n    private lateinit var workManager: WorkManager\n\n    @BeforeTest\n    fun setup() {\n        hiltRule.inject()\n\n        val context = InstrumentationRegistry.getInstrumentation().context\n\n        val workConfig = androidx.work.Configuration.Builder()\n            .setWorkerFactory(workerFactory)\n            .setMinimumLoggingLevel(Log.DEBUG)\n            .setExecutor(SynchronousExecutor())\n            .build()\n\n        WorkManagerTestInitHelper.initializeTestWorkManager(context, workConfig)\n        workManager = WorkManager.getInstance(context)\n\n        composeRule.setTestContent {\n            savedStateHandle = SavedStateHandle(\n                mapOf(\n                    WordleScreenNavArgs::rowLimit.name to 3,\n                    WordleScreenNavArgs::word.name to \"TEST\"\n                )\n            )\n\n            viewModel = WordleScreenViewModel(\n                wordleRepository = wordleRepository,\n                savedStateHandle = savedStateHandle,\n                workManager = workManager,\n                analyticsHelper = LocalDebugAnalyticsHelper()\n            )\n\n            val windowSizeClass = calculateWindowSizeClass(activity = composeRule.activity)\n\n            CompositionLocalProvider(LocalInspectionMode provides true) {\n                WordleScreen(\n                    wordleScreenViewModel = viewModel,\n                    navigator = EmptyDestinationsNavigator,\n                    windowSizeClass = windowSizeClass\n                )\n            }\n        }\n    }\n\n    @Test\n    fun wordleScreen_basicControls() {\n        composeRule.waitUntil {\n            !viewModel.uiState.value.loading\n        }\n\n        composeRule.onNodeWithTag(WordleScreenTestTags.VERIFY_FAB).assertDoesNotExist()\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .onChildren()\n            .assertCountEquals(WordleScreenUiState.ALL_LETTERS.length)\n            .assertAll(isEnabled())\n            .assertAll(hasClickAction())\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(1)\n            .onFirst()\n            .onChildren()\n            .assertAll(hasText(\" \"))\n            .assertAll(hasContentDescription(\"Item empty\"))\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .onChildren()\n            .onFirst()\n            .performClick()\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .onFirst()\n            .onChildren()\n            .filter(hasText(\" \"))\n            .assertCountEquals(3)\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .onFirst()\n            .onChildren()\n            .onFirst()\n            .assertTextEquals(\"Q\")\n            .assertContentDescriptionEquals(\"Item Q none\")\n            .performClick()\n            .assertTextEquals(\" \")\n            .assertContentDescriptionEquals(\"Item empty\")\n    }\n\n    @Test\n    fun wordleScreen_verifyWord() {\n        composeRule.waitUntil {\n            !viewModel.uiState.value.loading\n        }\n\n        composeRule.onNodeWithTag(WordleScreenTestTags.VERIFY_FAB).assertDoesNotExist()\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .onChildren()\n            .assertCountEquals(WordleScreenUiState.ALL_LETTERS.length)\n            .assertAll(isEnabled())\n            .assertAll(hasClickAction())\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(1)\n            .onFirst()\n            .onChildren()\n            .assertAll(hasText(\" \"))\n            .assertAll(hasContentDescription(\"Item empty\"))\n\n        val keyboardSemantics = composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .onChildren()\n\n        keyboardSemantics[WordleScreenUiState.ALL_LETTERS.indexOf('A')].performClick()\n        keyboardSemantics[WordleScreenUiState.ALL_LETTERS.indexOf('E')].performClick()\n        keyboardSemantics[WordleScreenUiState.ALL_LETTERS.indexOf('T')].performClick()\n        keyboardSemantics[WordleScreenUiState.ALL_LETTERS.indexOf('T')].performClick()\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.VERIFY_FAB)\n            .assertIsDisplayed()\n            .performClick()\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .onFirst()\n            .onChildren()\n            .filter(hasText(\" \"))\n            .assertCountEquals(0)\n\n        val rowFirstChildrenSemantics = composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .onFirst()\n            .onChildren()\n\n        rowFirstChildrenSemantics\n            .onFirst()\n            .assertTextEquals(\"A\")\n            .assertContentDescriptionEquals(\"Item A none\")\n\n        rowFirstChildrenSemantics[1]\n            .assertTextEquals(\"E\")\n            .assertContentDescriptionEquals(\"Item E correct\")\n\n        rowFirstChildrenSemantics[2]\n            .assertTextEquals(\"T\")\n            .assertContentDescriptionEquals(\"Item T present\")\n\n        rowFirstChildrenSemantics[3]\n            .assertTextEquals(\"T\")\n            .assertContentDescriptionEquals(\"Item T correct\")\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(2)\n            .onLast()\n            .onChildren()\n            .assertAll(hasText(\" \"))\n            .assertAll(hasContentDescription(\"Item empty\"))\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .onChildren()\n            .assertCountEquals(WordleScreenUiState.ALL_LETTERS.length)\n            .filter(hasText(\"A\"))\n            .assertAll(isNotEnabled())\n\n        composeRule.onNodeWithTag(WordleScreenTestTags.VERIFY_FAB).assertDoesNotExist()\n    }\n\n    @Test\n    fun wordleScreen_rowLimit() {\n        composeRule.waitUntil {\n            !viewModel.uiState.value.loading\n        }\n\n        composeRule.onNodeWithTag(WordleScreenTestTags.VERIFY_FAB).assertDoesNotExist()\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(1)\n            .onFirst()\n            .onChildren()\n            .assertAll(hasText(\" \"))\n            .assertAll(hasContentDescription(\"Item empty\"))\n\n        clickFirstKeyWordTimes()\n\n        composeRule.onNodeWithTag(WordleScreenTestTags.VERIFY_FAB)\n            .assertIsDisplayed()\n            .performClick()\n            .assertDoesNotExist()\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(2)\n\n        assert(!viewModel.uiState.value.isGamedEnded)\n\n        clickFirstKeyWordTimes()\n\n        composeRule.onNodeWithTag(WordleScreenTestTags.VERIFY_FAB)\n            .assertIsDisplayed()\n            .performClick()\n            .assertDoesNotExist()\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(3)\n\n        //assert(!viewModel.uiState.value.isGamedEnded)\n\n        clickFirstKeyWordTimes()\n\n        composeRule.onNodeWithTag(WordleScreenTestTags.VERIFY_FAB)\n            .assertIsDisplayed()\n            .performClick()\n            .assertDoesNotExist()\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(3)\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .assertDoesNotExist()\n\n        assert(viewModel.uiState.value.isGamedEnded)\n    }\n\n    @Test\n    fun wordleScreen_correctWord() {\n        composeRule.waitUntil {\n            !viewModel.uiState.value.loading\n        }\n\n        composeRule.onNodeWithTag(WordleScreenTestTags.VERIFY_FAB).assertDoesNotExist()\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .onChildren()\n            .assertCountEquals(WordleScreenUiState.ALL_LETTERS.length)\n            .assertAll(isEnabled())\n            .assertAll(hasClickAction())\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(1)\n            .onFirst()\n            .onChildren()\n            .assertAll(hasText(\" \"))\n            .assertAll(hasContentDescription(\"Item empty\"))\n\n        val keyboardSemantics = composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .onChildren()\n\n        keyboardSemantics[WordleScreenUiState.ALL_LETTERS.indexOf('T')].performClick()\n        keyboardSemantics[WordleScreenUiState.ALL_LETTERS.indexOf('E')].performClick()\n        keyboardSemantics[WordleScreenUiState.ALL_LETTERS.indexOf('S')].performClick()\n        keyboardSemantics[WordleScreenUiState.ALL_LETTERS.indexOf('T')].performClick()\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.VERIFY_FAB)\n            .performClick()\n            .assertDoesNotExist()\n\n        composeRule\n            .onAllNodesWithTag(WordleScreenTestTags.WORDLE_ROW)\n            .assertCountEquals(1)\n\n        composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .assertDoesNotExist()\n    }\n\n    private fun clickFirstKeyWordTimes() {\n        val tKeyIndex = WordleScreenUiState.ALL_LETTERS.indexOf('T')\n\n        val firstKey = composeRule\n            .onNodeWithTag(WordleScreenTestTags.KEYBOARD)\n            .onChildren()[tKeyIndex]\n\n        val wordLength = viewModel.uiState.value.word?.length ?: -1\n        assert(wordLength > 0)\n\n        repeat(wordLength) { firstKey.performClick() }\n    }\n}"
  },
  {
    "path": "wordle/src/androidTest/java/com/infinitepower/newquiz/wordle/components/WordleKeyBoardTest.kt",
    "content": "package com.infinitepower.newquiz.wordle.components\n\nimport androidx.compose.foundation.layout.ExperimentalLayoutApi\nimport androidx.compose.material3.windowsizeclass.WindowWidthSizeClass\nimport androidx.compose.ui.test.*\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport kotlin.test.assertTrue\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class WordleKeyBoardTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    @OptIn(ExperimentalLayoutApi::class)\n    fun testWordleKeyBoard_displayCorrectNumberOfItems() {\n        val keys = charArrayOf('a', 'b', 'c')\n\n        composeTestRule.setTestContent {\n            WordleKeyBoard(\n                keys = keys,\n                disabledKeys = emptySet(),\n                onKeyClick = {},\n                windowWidthSizeClass = WindowWidthSizeClass.Compact,\n                wordleQuizType = WordleQuizType.TEXT\n            )\n        }\n\n        composeTestRule\n            .onAllNodesWithTag(WordleKeyBoardTestingTags.KEY)\n            .assertCountEquals(3)\n    }\n\n    @Test\n    fun testWordleKeyBoardItem_displayCorrectKey() {\n        composeTestRule.setTestContent {\n            WordleKeyboardKey(\n                key = 'a',\n                disabled = true,\n                onKeyClick = {}\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(WordleKeyBoardTestingTags.KEY)\n            .assertIsDisplayed()\n            .assertIsNotEnabled()\n            .assertTextContains(\"a\")\n    }\n\n    @Test\n    fun testWordleKeyBoardItem_disabled() {\n        composeTestRule.setTestContent {\n            WordleKeyboardKey(\n                key = 'a',\n                disabled = true,\n                onKeyClick = {}\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(WordleKeyBoardTestingTags.KEY)\n            .assertIsDisplayed()\n            .assertIsNotEnabled()\n            .assertTextContains(\"a\")\n    }\n\n    @Test\n    fun testWordleKeyBoardItem_callsOnClickWhenClicked() {\n        var wasOnClickCalled = false\n\n        composeTestRule.setTestContent {\n            WordleKeyboardKey(\n                key = 'a',\n                disabled = false,\n                onKeyClick = { wasOnClickCalled = true }\n            )\n        }\n\n        composeTestRule\n            .onNodeWithText(\"a\")\n            .assertHasClickAction()\n            .performClick()\n\n        assertTrue(wasOnClickCalled, \"Button as not clicked\")\n    }\n}"
  },
  {
    "path": "wordle/src/androidTest/java/com/infinitepower/newquiz/wordle/components/WordleRowComponentTest.kt",
    "content": "package com.infinitepower.newquiz.wordle.components\n\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.test.*\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SmallTest\nimport com.infinitepower.newquiz.core.testing.utils.setTestContent\nimport com.infinitepower.newquiz.model.wordle.WordleChar\nimport com.infinitepower.newquiz.model.wordle.WordleItem\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@SmallTest\n@RunWith(AndroidJUnit4::class)\ninternal class WordleRowComponentTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun wordleComponent_emptyWordleItem_test() {\n        val item = WordleItem.Empty\n\n        composeTestRule.setTestContent {\n            WordleComponent(\n                item = item,\n                onClick = {}\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(WordleRowComponentTestingTags.WORDLE_COMPONENT_SURFACE)\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsNotEnabled()\n            .assertHasClickAction()\n            .assertTextEquals(\" \")\n            .assertContentDescriptionEquals(\"Item empty\")\n    }\n\n    @Test\n    fun wordleComponent_noneWordleItem_test() {\n        composeTestRule.setTestContent {\n            val (item, setItem) = remember {\n                mutableStateOf<WordleItem>(WordleItem.None(WordleChar('A')))\n            }\n\n            WordleComponent(\n                item = item,\n                onClick = { setItem(WordleItem.Empty) }\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(WordleRowComponentTestingTags.WORDLE_COMPONENT_SURFACE)\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsEnabled()\n            .assertHasClickAction()\n            .assertTextEquals(\"A\")\n            .assertContentDescriptionEquals(\"Item A none\")\n            .performClick()\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsNotEnabled()\n            .assertHasClickAction()\n            .assertTextEquals(\" \")\n            .assertContentDescriptionEquals(\"Item empty\")\n    }\n\n    @Test\n    fun wordleComponent_presentWordleItem_test() {\n        composeTestRule.setTestContent {\n            WordleComponent(\n                item = WordleItem.Present(WordleChar('A')),\n                onClick = {}\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(WordleRowComponentTestingTags.WORDLE_COMPONENT_SURFACE)\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsNotEnabled()\n            .assertHasClickAction()\n            .assertTextEquals(\"A\")\n            .assertContentDescriptionEquals(\"Item A present\")\n    }\n\n    @Test\n    fun wordleComponent_correctWordleItem_test() {\n        composeTestRule.setTestContent {\n            WordleComponent(\n                item = WordleItem.Correct(WordleChar('A')),\n                onClick = {}\n            )\n        }\n\n        composeTestRule\n            .onNodeWithTag(WordleRowComponentTestingTags.WORDLE_COMPONENT_SURFACE)\n            .assertExists()\n            .assertIsDisplayed()\n            .assertIsNotEnabled()\n            .assertHasClickAction()\n            .assertTextEquals(\"A\")\n            .assertContentDescriptionEquals(\"Item A correct\")\n    }\n}"
  },
  {
    "path": "wordle/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n    <application>\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            tools:node=\"remove\" />\n    </application>\n</manifest>"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/WordleScreen.kt",
    "content": "package com.infinitepower.newquiz.wordle\n\nimport androidx.annotation.Keep\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.ExperimentalLayoutApi\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.LazyListScope\nimport androidx.compose.foundation.lazy.LazyListState\nimport androidx.compose.foundation.lazy.itemsIndexed\nimport androidx.compose.foundation.lazy.rememberLazyListState\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.rounded.Check\nimport androidx.compose.material.icons.rounded.Info\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.ExtendedFloatingActionButton\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedButton\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.WindowHeightSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowWidthSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.LocalConfiguration\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewScreenSizes\nimport androidx.compose.ui.unit.DpSize\nimport androidx.compose.ui.unit.dp\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport androidx.navigation.NavController\nimport com.infinitepower.newquiz.core.navigation.MazeNavigator\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.animationsEnabled\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.core.ui.components.icon.button.BackIconButton\nimport com.infinitepower.newquiz.core.util.asString\nimport com.infinitepower.newquiz.data.util.translation.getWordleTitle\nimport com.infinitepower.newquiz.model.wordle.WordleChar\nimport com.infinitepower.newquiz.model.wordle.WordleItem\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleRowItem\nimport com.infinitepower.newquiz.wordle.components.InfoDialog\nimport com.infinitepower.newquiz.wordle.components.WordleKeyBoard\nimport com.infinitepower.newquiz.wordle.components.WordleRowComponent\nimport com.infinitepower.newquiz.wordle.destinations.WordleScreenDestination\nimport com.ramcosta.composedestinations.annotation.DeepLink\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport kotlinx.coroutines.delay\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Keep\ndata class WordleScreenNavArgs(\n    val rowLimit: Int? = null,\n    val word: String? = null,\n    val quizType: WordleQuizType = WordleQuizType.TEXT,\n    val mazeItemId: String? = null,\n    val textHelper: String? = null\n)\n\n@Composable\n@Destination(\n    navArgsDelegate = WordleScreenNavArgs::class,\n    deepLinks = [\n        DeepLink(uriPattern = \"newquiz://wordleinfinite\")\n    ]\n)\nfun WordleScreen(\n    navigator: DestinationsNavigator,\n    mazeNavigator: MazeNavigator,\n    navController: NavController,\n    windowSizeClass: WindowSizeClass,\n    wordleScreenViewModel: WordleScreenViewModel = hiltViewModel()\n) {\n    val uiState by wordleScreenViewModel.uiState.collectAsStateWithLifecycle()\n\n    val mazeItemId = remember(navController) {\n        val backStackEntry = navController.getBackStackEntry(WordleScreenDestination.route)\n        val args = WordleScreenDestination.argsFrom(backStackEntry)\n        args.mazeItemId?.toIntOrNull()\n    }\n\n    WordleScreenImpl(\n        fromMaze = mazeItemId != null,\n        uiState = uiState,\n        onEvent = wordleScreenViewModel::onEvent,\n        onBackClick = navigator::popBackStack,\n        windowSizeClass = windowSizeClass\n    )\n\n    // If the game is over and is from maze, navigate to maze results\n    LaunchedEffect(uiState.isGamedEnded) {\n        if (uiState.isGamedEnded && mazeItemId != null) {\n            if (!uiState.isGameOver) {\n                // Show a delay before moving to maze results\n                delay(NAV_TO_RESULTS_DELAY_MILLIS)\n                mazeNavigator.navigateToMazeResults(mazeItemId)\n            }\n        }\n    }\n}\n\n@Composable\n@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)\nprivate fun WordleScreenImpl(\n    fromMaze: Boolean = false,\n    uiState: WordleScreenUiState,\n    onEvent: (event: WordleScreenUiEvent) -> Unit,\n    onBackClick: () -> Unit,\n    windowSizeClass: WindowSizeClass,\n    animationsEnabled: Boolean = MaterialTheme.animationsEnabled.wordle\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val (gameOverPopupVisible, setGameOverPopupVisibility) = remember(uiState.isGameOver && !fromMaze) {\n        mutableStateOf(uiState.isGameOver)\n    }\n\n    val (infoDialogVisible, setInfoDialogVisibility) = remember {\n        mutableStateOf(false)\n    }\n\n    val screenTitle = uiState.wordleQuizType.getWordleTitle()\n\n    val rowLayout = remember(windowSizeClass) {\n        windowSizeClass.heightSizeClass == WindowHeightSizeClass.Compact\n                && windowSizeClass.widthSizeClass > WindowWidthSizeClass.Compact\n    }\n\n    val keyboardBottomPadding = if (!rowLayout) {\n        MaterialTheme.spacing.extraLarge\n    } else 0.dp\n\n    val scrollState = rememberLazyListState()\n\n    LaunchedEffect(key1 = uiState.currentRowPosition) {\n        if (uiState.currentRowPosition > 0 && uiState.rows.isNotEmpty()) {\n            scrollState.animateScrollToItem(uiState.rows.lastIndex)\n        }\n    }\n\n    Scaffold(\n        topBar = {\n            TopAppBar(\n                title = { Text(text = screenTitle.asString()) },\n                navigationIcon = { BackIconButton(onClick = onBackClick) },\n                actions = {\n                    IconButton(onClick = { setInfoDialogVisibility(true) }) {\n                        Icon(\n                            imageVector = Icons.Rounded.Info,\n                            contentDescription = stringResource(id = CoreR.string.info)\n                        )\n                    }\n                }\n            )\n        },\n        floatingActionButton = {\n            if (uiState.currentRowCompleted && !uiState.isGamedEnded) {\n                ExtendedFloatingActionButton(\n                    text = {\n                        Text(text = stringResource(id = CoreR.string.verify))\n                    },\n                    icon = {\n                        Icon(\n                            imageVector = Icons.Rounded.Check,\n                            contentDescription = stringResource(id = CoreR.string.verify)\n                        )\n                    },\n                    onClick = {\n                        onEvent(WordleScreenUiEvent.VerifyRow)\n                    },\n                    modifier = Modifier.testTag(WordleScreenTestTags.VERIFY_FAB)\n                )\n            }\n        }\n    ) { innerPadding ->\n        WordleContainer(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(innerPadding),\n            rowLayout = rowLayout,\n            wordsScrollState = scrollState,\n            wordleContent = {\n                if (uiState.loading) {\n                    item {\n                        CircularProgressIndicator(\n                            modifier = Modifier.testTag(\n                                WordleScreenTestTags.LOADING_PROGRESS_INDICATOR\n                            )\n                        )\n                    }\n                }\n\n                if (uiState.textHelper != null) {\n                    item {\n                        Text(\n                            text = uiState.textHelper,\n                            style = MaterialTheme.typography.titleMedium,\n                            modifier = Modifier.padding(horizontal = spaceMedium)\n                        )\n                    }\n                }\n\n                itemsIndexed(items = uiState.rows) { index, rowItem ->\n                    val isCurrentRow = uiState.currentRowPosition == index\n\n                    WordleRowComponent(\n                        wordleRowItem = rowItem,\n                        word = uiState.word.orEmpty(),\n                        onItemClick = { itemIndex ->\n                            onEvent(\n                                WordleScreenUiEvent.OnRemoveKeyClick(\n                                    itemIndex\n                                )\n                            )\n                        },\n                        isColorBlindEnabled = uiState.isColorBlindEnabled,\n                        isLetterHintsEnabled = uiState.isLetterHintEnabled,\n                        modifier = Modifier.testTag(WordleScreenTestTags.WORDLE_ROW),\n                        enabled = isCurrentRow,\n                        animationEnabled = animationsEnabled\n                    )\n                }\n            },\n            keyboardContent = {\n                if (!uiState.loading && !uiState.isGamedEnded) {\n                    WordleKeyBoard(\n                        modifier = Modifier\n                            // .padding(horizontal = MaterialTheme.spacing.small)\n                            // .padding(bottom = keyboardBottomPadding)\n                            .testTag(WordleScreenTestTags.KEYBOARD),\n                        rowLayout = rowLayout,\n                        keys = uiState.wordleKeys,\n                        disabledKeys = uiState.keysDisabled,\n                        onKeyClick = { key ->\n                            onEvent(WordleScreenUiEvent.OnKeyClick(key))\n                        },\n                        wordleQuizType = uiState.wordleQuizType ?: WordleQuizType.TEXT,\n                        windowWidthSizeClass = windowSizeClass.widthSizeClass,\n                        contentPadding = PaddingValues(\n                            start = MaterialTheme.spacing.small,\n                            end = MaterialTheme.spacing.small,\n                            bottom = keyboardBottomPadding\n                        )\n                    )\n                }\n\n                if (uiState.isGamedEnded && !fromMaze) {\n                    Row(\n                        verticalAlignment = Alignment.CenterVertically,\n                        horizontalArrangement = Arrangement.spacedBy(spaceMedium),\n                        modifier = Modifier.padding(spaceMedium)\n                    ) {\n                        OutlinedButton(\n                            onClick = { onEvent(WordleScreenUiEvent.OnPlayAgainClick) },\n                            modifier = Modifier.weight(1f)\n                        ) {\n                            Text(text = stringResource(id = CoreR.string.play_again))\n                        }\n\n                        Button(\n                            onClick = onBackClick,\n                            modifier = Modifier.weight(1f)\n                        ) {\n                            Text(text = stringResource(id = CoreR.string.back))\n                        }\n                    }\n                }\n            }\n        )\n    }\n\n    if (gameOverPopupVisible) {\n        AlertDialog(\n            onDismissRequest = { setGameOverPopupVisibility(false) },\n            title = {\n                Text(text = stringResource(id = CoreR.string.game_over))\n            },\n            confirmButton = {\n                TextButton(onClick = { setGameOverPopupVisibility(false) }) {\n                    Text(text = stringResource(id = CoreR.string.close))\n                }\n            }\n        )\n    }\n\n    if (infoDialogVisible) {\n        InfoDialog(\n            isColorBlindEnabled = uiState.isColorBlindEnabled,\n            onDismissRequest = { setInfoDialogVisibility(false) }\n        )\n    }\n}\n\n@Composable\nprivate fun WordleContainer(\n    modifier: Modifier = Modifier,\n    rowLayout: Boolean = false,\n    wordsScrollState: LazyListState = rememberLazyListState(),\n    wordleContent: LazyListScope.() -> Unit,\n    keyboardContent: @Composable BoxScope.() -> Unit\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    if (rowLayout) {\n        Row(\n            modifier = modifier.fillMaxSize(),\n            verticalAlignment = Alignment.CenterVertically,\n            horizontalArrangement = Arrangement.spacedBy(spaceMedium)\n        ) {\n            LazyColumn(\n                modifier = Modifier.weight(1f),\n                verticalArrangement = Arrangement.spacedBy(spaceMedium),\n                horizontalAlignment = Alignment.CenterHorizontally,\n                contentPadding = PaddingValues(vertical = spaceMedium),\n                content = wordleContent,\n                state = wordsScrollState\n            )\n\n            Box(\n                modifier = Modifier.weight(1f),\n                contentAlignment = Alignment.Center\n            ) {\n                keyboardContent()\n            }\n        }\n    } else {\n        Column(\n            modifier = modifier.fillMaxSize(),\n            horizontalAlignment = Alignment.CenterHorizontally\n        ) {\n            LazyColumn(\n                modifier = Modifier\n                    .weight(2f)\n                    .fillMaxWidth(),\n                verticalArrangement = Arrangement.spacedBy(spaceMedium),\n                horizontalAlignment = Alignment.CenterHorizontally,\n                contentPadding = PaddingValues(bottom = spaceMedium),\n                content = wordleContent,\n                state = wordsScrollState\n            )\n\n            Box(\n                modifier = Modifier.weight(1f),\n                contentAlignment = Alignment.Center\n            ) {\n                keyboardContent()\n            }\n        }\n    }\n}\n\nprivate const val NAV_TO_RESULTS_DELAY_MILLIS = 1000L\n\n@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)\ninternal object WordleScreenTestTags {\n    const val VERIFY_FAB = \"VERIFY_FAB\"\n    const val LOADING_PROGRESS_INDICATOR = \"LOADING_PROGRESS_INDICATOR\"\n    const val KEYBOARD = \"KEYBOARD\"\n    const val WORDLE_ROW = \"WORDLE_ROW\"\n}\n\n@Composable\n@PreviewScreenSizes\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)\nprivate fun WordleScreenPreview() {\n    val rowItems = listOf(\n        WordleRowItem(\n            items = listOf(\n                WordleItem.Correct(char = WordleChar('Q')),\n                WordleItem.None(char = WordleChar('A')),\n                WordleItem.Present(char = WordleChar('Z')),\n                WordleItem.Present(char = WordleChar('I')),\n                WordleItem.Empty\n            )\n        )\n    )\n\n    val configuration = LocalConfiguration.current\n    val screenHeight = configuration.screenHeightDp.dp\n    val screenWidth = configuration.screenWidthDp.dp\n    val windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(screenWidth, screenHeight))\n\n    NewQuizTheme {\n        WordleScreenImpl(\n            uiState = WordleScreenUiState(\n                word = \"QUIZZ\",\n                rows = rowItems,\n                currentRowPosition = 0,\n                loading = false,\n                wordleQuizType = WordleQuizType.TEXT,\n                textHelper = \"Wordle text helper for word.\",\n            ),\n            onEvent = {},\n            onBackClick = {},\n            windowSizeClass = windowSizeClass,\n            animationsEnabled = false\n        )\n    }\n}\n"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/WordleScreenUiEvent.kt",
    "content": "package com.infinitepower.newquiz.wordle\n\nsealed interface WordleScreenUiEvent {\n    data class OnKeyClick(val key: Char) : WordleScreenUiEvent\n\n    data class OnRemoveKeyClick(val index: Int) : WordleScreenUiEvent\n\n    data object VerifyRow : WordleScreenUiEvent\n\n    data object OnPlayAgainClick : WordleScreenUiEvent\n}\n"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/WordleScreenUiState.kt",
    "content": "package com.infinitepower.newquiz.wordle\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleRowItem\n\n@Keep\ndata class WordleScreenUiState(\n    val loading: Boolean = true,\n    val word: String? = null,\n    val rowLimit: Int = Int.MAX_VALUE,\n    val rows: List<WordleRowItem> = emptyList(),\n    val currentRowPosition: Int = -1,\n    val keysDisabled: Set<Char> = emptySet(),\n    val isColorBlindEnabled: Boolean = false,\n    val isLetterHintEnabled: Boolean = false,\n    val isHardModeEnabled: Boolean = false,\n    val wordleQuizType: WordleQuizType? = null,\n    val textHelper: String? = null\n) {\n    companion object {\n        const val ALL_LETTERS = \"QWERTYUIOPASDFGHJKLZXCVBNM\"\n\n        const val allNumbers = \"1234567890\"\n\n        const val mathFormulaKeys = \"0123456789+-*/=\"\n    }\n\n    val currentRowCompleted: Boolean\n        get() = rows\n            .lastOrNull()\n            ?.isRowCompleted == true\n\n    private val currentRowCorrect: Boolean\n        get() = rows\n            .lastOrNull { it.isRowVerified }\n            ?.isRowCorrect == true\n\n    val isGamedEnded: Boolean\n        get() = !loading && currentRowPosition + 1 > rowLimit || currentRowCorrect\n\n    val isGameOver: Boolean\n        get() = isGamedEnded && !currentRowCorrect\n\n    val wordleKeys: CharArray\n        get() = when (wordleQuizType) {\n            WordleQuizType.TEXT -> ALL_LETTERS.toCharArray()\n            WordleQuizType.NUMBER, WordleQuizType.NUMBER_TRIVIA -> allNumbers.toList().toCharArray()\n            WordleQuizType.MATH_FORMULA -> mathFormulaKeys.toCharArray()\n            null -> charArrayOf()\n        }\n}"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/WordleScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.wordle\n\nimport android.util.Log\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport androidx.work.OneTimeWorkRequestBuilder\nimport androidx.work.WorkManager\nimport androidx.work.workDataOf\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.ui.SnackbarController\nimport com.infinitepower.newquiz.core.util.collections.indexOfFirstOrNull\nimport com.infinitepower.newquiz.data.repository.wordle.InvalidWordError\nimport com.infinitepower.newquiz.data.worker.UpdateGlobalEventDataWorker\nimport com.infinitepower.newquiz.domain.repository.maze.MazeQuizRepository\nimport com.infinitepower.newquiz.domain.repository.wordle.WordleRepository\nimport com.infinitepower.newquiz.model.Resource\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport com.infinitepower.newquiz.model.wordle.WordleItem\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.model.wordle.WordleRowItem\nimport com.infinitepower.newquiz.model.wordle.emptyRowItem\nimport com.infinitepower.newquiz.model.wordle.itemsToString\nimport com.infinitepower.newquiz.wordle.util.asUiText\nimport com.infinitepower.newquiz.wordle.util.word.containsAllLastRevealedHints\nimport com.infinitepower.newquiz.wordle.util.word.getKeysDisabled\nimport com.infinitepower.newquiz.wordle.util.word.verifyFromWord\nimport com.infinitepower.newquiz.wordle.util.worker.WordleEndGameWorker\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.flow.updateAndGet\nimport kotlinx.coroutines.launch\nimport javax.inject.Inject\nimport com.infinitepower.newquiz.core.R as CoreR\n\nprivate const val TAG = \"WordleScreenViewModel\"\n\n@HiltViewModel\nclass WordleScreenViewModel @Inject constructor(\n    private val wordleRepository: WordleRepository,\n    savedStateHandle: SavedStateHandle,\n    private val workManager: WorkManager,\n    private val analyticsHelper: AnalyticsHelper,\n    private val mazeQuizRepository: MazeQuizRepository\n) : ViewModel() {\n    private val navArgs: WordleScreenNavArgs = savedStateHandle.navArgs()\n\n    private var _uiState = MutableStateFlow(WordleScreenUiState())\n    val uiState = _uiState.asStateFlow()\n\n    init {\n        viewModelScope.launch {\n            val isColorBlindEnabled = wordleRepository.isColorBlindEnabled()\n            val isLetterHintEnabled = wordleRepository.isLetterHintEnabled()\n            val isHardModeEnabled = wordleRepository.isHardModeEnabled()\n\n            _uiState.update { currentState ->\n                currentState.copy(\n                    isColorBlindEnabled = isColorBlindEnabled,\n                    isLetterHintEnabled = isLetterHintEnabled,\n                    isHardModeEnabled = isHardModeEnabled\n                )\n            }\n        }\n\n        generateGame()\n    }\n\n//    override fun onCleared() {\n//        endGame()\n//        super.onCleared()\n//    }\n\n    fun onEvent(event: WordleScreenUiEvent) {\n        when (event) {\n            is WordleScreenUiEvent.OnKeyClick -> addKeyToCurrentRow(event.key)\n            is WordleScreenUiEvent.OnRemoveKeyClick -> removeKeyFromCurrentRow(event.index)\n            is WordleScreenUiEvent.VerifyRow -> verifyRow()\n            is WordleScreenUiEvent.OnPlayAgainClick -> generateGame()\n        }\n    }\n\n    private fun generateGame() = viewModelScope.launch(Dispatchers.IO) {\n        val quizType = navArgs.quizType\n\n        // Checks if saved state has an initial word.\n        // If has, generate the rows with the word, if not create a new word.\n        if (navArgs.word != null) {\n            generateRows(navArgs.word, quizType, navArgs.textHelper)\n            return@launch\n        }\n\n        wordleRepository\n            .generateRandomWord(quizType)\n            .collect { res ->\n                if (res is Resource.Loading) {\n                    _uiState.update { currentState ->\n                        currentState.copy(loading = true)\n                    }\n                }\n\n                if (res is Resource.Success) {\n                    res.data?.let { wordleWord ->\n                        generateRows(\n                            word = wordleWord.word,\n                            quizType = quizType,\n                            textHelper = wordleWord.textHelper\n                        )\n                    }\n                }\n            }\n    }\n\n    private suspend fun generateRows(\n        word: String,\n        quizType: WordleQuizType,\n        textHelper: String? = null\n    ) {\n        Log.d(TAG, \"Word: $word\")\n\n        val rows = List(1) {\n            emptyRowItem(size = word.length)\n        }\n\n        // Gets the wordle max rows if the row limit from args is null, if not use the args row limit.\n        val rowLimit = wordleRepository.getWordleMaxRows(navArgs.rowLimit)\n\n        viewModelScope.launch {\n            UpdateGlobalEventDataWorker.enqueueWork(\n                workManager = workManager,\n                GameEvent.Wordle.PlayWordWithCategory(quizType)\n            )\n\n            analyticsHelper.logEvent(\n                AnalyticsEvent.WordleGameStart(\n                    wordLength = word.length,\n                    maxRows = rowLimit,\n                    category = quizType.name,\n                    mazeItemId = navArgs.mazeItemId?.toIntOrNull()\n                )\n            )\n        }\n\n        _uiState.update { currentState ->\n            currentState.copy(\n                loading = false,\n                word = word,\n                rowLimit = rowLimit,\n                rows = rows,\n                currentRowPosition = 0,\n                keysDisabled = emptySet(),\n                wordleQuizType = quizType,\n                textHelper = textHelper\n            )\n        }\n    }\n\n    private fun addKeyToCurrentRow(key: Char) {\n        _uiState.update { currentState ->\n            val newRows = currentState\n                .rows\n                .toMutableList()\n                .apply {\n                    val currentRowItem = this[currentState.currentRowPosition]\n\n                    val newRow = currentRowItem.items.toMutableList().apply {\n                        val emptyIndex =\n                            indexOfFirstOrNull { item -> item is WordleItem.Empty } ?: return\n\n                        set(emptyIndex, WordleItem.fromChar(key))\n                    }\n\n                    set(currentState.currentRowPosition, WordleRowItem(newRow))\n                }\n\n            currentState.copy(rows = newRows)\n        }\n    }\n\n    private fun removeKeyFromCurrentRow(index: Int) {\n        _uiState.update { currentState ->\n            val newRows = currentState\n                .rows\n                .toMutableList()\n                .apply {\n                    val currentRowItem = this[currentState.currentRowPosition]\n\n                    val newRow = currentRowItem.items.toMutableList().apply {\n                        set(index, WordleItem.Empty)\n                    }\n\n                    set(currentState.currentRowPosition, WordleRowItem(newRow))\n                }\n\n            currentState.copy(rows = newRows)\n        }\n    }\n\n    private fun verifyRow() {\n        val state = _uiState.updateAndGet { currentState ->\n            if (currentState.word == null) return\n            if (currentState.wordleQuizType == null) return\n            if (!currentState.currentRowCompleted) return\n\n            // Get current row items, if the current row is null stop the verification.\n            // Current row is the last row of the list.\n            val currentItems = currentState.rows.lastOrNull()?.items ?: return\n\n            wordleRepository.validateWord(\n                currentItems.itemsToString(),\n                currentState.wordleQuizType\n            ).onFailure { exception ->\n                viewModelScope.launch {\n                    if (exception is InvalidWordError) {\n                        SnackbarController.sendShortMessage(exception.asUiText())\n                    } else {\n                        SnackbarController.sendShortMessage(\n                            UiText.StringResource(CoreR.string.error_verifying_word)\n                        )\n                    }\n                }\n\n                return@updateAndGet currentState.copy()\n            }\n\n            // Verifies items with the word and verifies if the word is correct\n            val verifiedItems = currentItems verifyFromWord currentState.word\n\n            if (currentState.isHardModeEnabled) {\n                val last2RowItems = currentState\n                    .rows\n                    .getOrNull(currentState.rows.lastIndex - 1)\n                    ?.items\n                    .orEmpty()\n                    .filter { it is WordleItem.Correct || it is WordleItem.Present }\n\n                val containsAllLastRevealedHints =\n                    verifiedItems containsAllLastRevealedHints last2RowItems\n\n                if (!containsAllLastRevealedHints) {\n                    viewModelScope.launch {\n                        SnackbarController.sendShortMessage(\n                            UiText.StringResource(CoreR.string.need_to_use_all_hints_error)\n                        )\n                    }\n                    return@updateAndGet currentState.copy()\n                }\n            }\n\n            val isRowCorrect = verifiedItems.all { it is WordleItem.Correct }\n\n            val newRowPosition = currentState.currentRowPosition + 1\n\n            val newRows = currentState\n                .rows\n                .toMutableList()\n                .apply {\n                    // Updates the current row with the verified items\n                    set(currentState.currentRowPosition, WordleRowItem(verifiedItems))\n\n                    // Checks if is game end.\n                    // Is game end if new row position >= row limit and current row is correct.\n                    // If is not game end add new empty row.\n                    val gameEnd = newRowPosition >= currentState.rowLimit || isRowCorrect\n                    if (!gameEnd) add(emptyRowItem(currentState.word.length))\n                }\n\n            val keysDisabled = verifiedItems.getKeysDisabled()\n\n            currentState.copy(\n                currentRowPosition = newRowPosition,\n                rows = newRows,\n                keysDisabled = currentState.keysDisabled + keysDisabled,\n            )\n        }\n\n        if (state.isGamedEnded) {\n            endGame(state)\n        }\n    }\n\n    private fun endGame(currentState: WordleScreenUiState) {\n        val isLastRowCorrect = currentState.rows.lastOrNull()?.isRowCorrect == true\n\n        val mazeItemId = navArgs.mazeItemId\n\n        if (mazeItemId != null && isLastRowCorrect) {\n            viewModelScope.launch {\n                mazeQuizRepository.completeMazeItem(mazeItemId.toInt())\n            }\n        }\n\n        val wordleEndGameWorkRequest = OneTimeWorkRequestBuilder<WordleEndGameWorker>()\n            .setInputData(\n                workDataOf(\n                    WordleEndGameWorker.INPUT_WORD to currentState.word,\n                    WordleEndGameWorker.INPUT_ROW_LIMIT to currentState.rowLimit,\n                    WordleEndGameWorker.INPUT_CURRENT_ROW_POSITION to currentState.currentRowPosition,\n                    WordleEndGameWorker.INPUT_IS_LAST_ROW_CORRECT to isLastRowCorrect,\n                    WordleEndGameWorker.INPUT_QUIZ_TYPE to currentState.wordleQuizType?.name,\n                    WordleEndGameWorker.INPUT_MAZE_TEM_ID to mazeItemId\n                )\n            ).build()\n\n        workManager.enqueue(wordleEndGameWorkRequest)\n    }\n}\n"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/components/InfoDialog.kt",
    "content": "package com.infinitepower.newquiz.wordle.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.Card\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.text.SpanStyle\nimport androidx.compose.ui.text.buildAnnotatedString\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.withStyle\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.tooling.preview.PreviewParameter\nimport androidx.compose.ui.unit.sp\nimport com.infinitepower.newquiz.core.R\nimport com.infinitepower.newquiz.core.common.compose.preview.BooleanPreviewParameterProvider\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.wordle.WordleChar\nimport com.infinitepower.newquiz.model.wordle.WordleItem\nimport com.infinitepower.newquiz.model.wordle.WordleRowItem\n\n@Composable\ninternal fun InfoDialog(\n    isColorBlindEnabled: Boolean,\n    onDismissRequest: () -> Unit\n) {\n    // Word: QUIZ\n    val rowItem = WordleRowItem(\n        items = listOf(\n            WordleItem.fromChar('Q'), // None\n            WordleItem.Present(WordleChar('U')),\n            WordleItem.Correct(WordleChar('I')),\n            WordleItem.Present(WordleChar('Z')),\n        )\n    )\n\n    AlertDialog(\n        onDismissRequest = onDismissRequest,\n        title = {\n            Text(text = stringResource(id = R.string.info))\n        },\n        text = {\n            LazyColumn(\n                horizontalAlignment = Alignment.CenterHorizontally\n            ) {\n                item {\n                    WordleRowComponent(\n                        wordleRowItem = rowItem,\n                        word = \"QUIZ\",\n                        enabled = false, // Disable click\n                        animationEnabled = false,\n                        isColorBlindEnabled = isColorBlindEnabled,\n                        onItemClick = {}\n                    )\n                }\n\n                item { Spacer(modifier = Modifier.padding(MaterialTheme.spacing.medium)) }\n\n                item {\n                    InfoDialogCard(isColorBlindEnabled = isColorBlindEnabled)\n                }\n            }\n        },\n        confirmButton = {\n            TextButton(onClick = onDismissRequest) {\n                Text(text = stringResource(id = R.string.close))\n            }\n        }\n    )\n}\n\n@Composable\nprivate fun InfoDialogCard(\n    isColorBlindEnabled: Boolean\n) {\n    val spaceMedium = MaterialTheme.spacing.medium\n\n    val presentBackgroundColor = getItemRowBackgroundColor(\n        item = WordleItem.Present(WordleChar('C')),\n        isColorBlindEnabled = isColorBlindEnabled\n    )\n    val presentTextColor = getItemRowTextColor(\n        item = WordleItem.Present(WordleChar('C')),\n        isColorBlindEnabled = isColorBlindEnabled\n    )\n\n    val correctBackgroundColor = getItemRowBackgroundColor(\n        item = WordleItem.Correct(WordleChar('C')),\n        isColorBlindEnabled = isColorBlindEnabled\n    )\n    val correctTextColor = getItemRowTextColor(\n        item = WordleItem.Correct(WordleChar('C')),\n        isColorBlindEnabled = isColorBlindEnabled\n    )\n\n    Card {\n        Column(\n            modifier = Modifier.padding(spaceMedium),\n            verticalArrangement = Arrangement.spacedBy(spaceMedium)\n        ) {\n            // Char none: Q\n            Text(\n                buildAnnotatedString {\n                    withStyle(\n                        style = SpanStyle(\n                            fontWeight = FontWeight.Bold,\n                            fontSize = ITEM_DESCRIPTION_FONT_SIZE,\n                        )\n                    ) {\n                        append('Q')\n                    }\n                    append(stringResource(id = R.string.is_not_in_the_target_word_wordle))\n                }\n            )\n\n            // Chars present: U, Z\n            Text(\n                buildAnnotatedString {\n                    withStyle(\n                        style = SpanStyle(\n                            fontWeight = FontWeight.Bold,\n                            background = presentBackgroundColor,\n                            color = presentTextColor,\n                            fontSize = ITEM_DESCRIPTION_FONT_SIZE,\n                        )\n                    ) {\n                        append(\" U \")\n                    }\n                    append(\" , \")\n                    withStyle(\n                        style = SpanStyle(\n                            fontWeight = FontWeight.Bold,\n                            background = presentBackgroundColor,\n                            color = presentTextColor,\n                            fontSize = ITEM_DESCRIPTION_FONT_SIZE\n                        )\n                    ) {\n                        append(\" Z \")\n                    }\n                    append(stringResource(id = R.string.is_in_the_word_but_in_the_wrong_spot_wordle))\n                }\n            )\n\n            // Char correct: I\n            Text(\n                buildAnnotatedString {\n                    withStyle(\n                        style = SpanStyle(\n                            fontWeight = FontWeight.Bold,\n                            background = correctBackgroundColor,\n                            color = correctTextColor,\n                            fontSize = ITEM_DESCRIPTION_FONT_SIZE\n                        )\n                    ) {\n                        append(\" I \")\n                    }\n                    append(stringResource(id = R.string.is_in_the_word_and_in_the_correct_spot_wordle))\n                }\n            )\n        }\n    }\n}\n\nprivate val ITEM_DESCRIPTION_FONT_SIZE = 18.sp\n\n@Composable\n@PreviewLightDark\nprivate fun InfoDialogPreview(\n    @PreviewParameter(BooleanPreviewParameterProvider::class) isColorBlindEnabled: Boolean\n) {\n    NewQuizTheme {\n        InfoDialog(\n            isColorBlindEnabled = isColorBlindEnabled,\n            onDismissRequest = {}\n        )\n    }\n}\n"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/components/WordleKeyBoard.kt",
    "content": "package com.infinitepower.newquiz.wordle.components\n\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.ExperimentalLayoutApi\nimport androidx.compose.foundation.layout.FlowRow\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.lazy.grid.GridCells\nimport androidx.compose.foundation.lazy.grid.LazyVerticalGrid\nimport androidx.compose.foundation.lazy.grid.items\nimport androidx.compose.material3.Card\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi\nimport androidx.compose.material3.windowsizeclass.WindowSizeClass\nimport androidx.compose.material3.windowsizeclass.WindowWidthSizeClass\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.LocalConfiguration\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.DpSize\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\n\n/**\n * A keyboard composed of keys that can be clicked.\n *\n * @param modifier the modifier to apply to the keyboard\n * @param keys the keys to include on the keyboard\n * @param disabledKeys a set of keys that should be disabled and not clickable\n * @param onKeyClick a callback to be invoked when a key is clicked\n */\n@Composable\n@ExperimentalLayoutApi\ninternal fun WordleKeyBoard(\n    modifier: Modifier = Modifier,\n    rowLayout: Boolean = false,\n    windowWidthSizeClass: WindowWidthSizeClass,\n    wordleQuizType: WordleQuizType,\n    keys: CharArray,\n    disabledKeys: Set<Char>,\n    contentPadding: PaddingValues = PaddingValues(),\n    onKeyClick: (key: Char) -> Unit\n) {\n    val spaceSmall = MaterialTheme.spacing.small\n\n    val maxWidth = when {\n        windowWidthSizeClass == WindowWidthSizeClass.Medium && !rowLayout -> 0.5f\n        windowWidthSizeClass == WindowWidthSizeClass.Expanded && !rowLayout -> 0.35f\n        else -> 1f\n    }\n\n    if (wordleQuizType == WordleQuizType.TEXT) {\n        FlowRow(\n            modifier = modifier\n                .padding(contentPadding)\n                .fillMaxWidth(maxWidth),\n            verticalArrangement = Arrangement.spacedBy(spaceSmall),\n            horizontalArrangement = Arrangement.spacedBy(spaceSmall, Alignment.CenterHorizontally)\n        ) {\n            keys.forEach { key ->\n                WordleKeyboardKey(\n                    key = key,\n                    disabled = key in disabledKeys,\n                    onKeyClick = { onKeyClick(key) }\n                )\n            }\n        }\n    } else {\n        val chuckSize = if (wordleQuizType == WordleQuizType.NUMBER) 3 else 4\n\n        val keyList = keys.toList()\n\n        LazyVerticalGrid(\n            columns = GridCells.Fixed(chuckSize),\n            modifier = modifier.fillMaxWidth(maxWidth),\n            userScrollEnabled = false,\n            horizontalArrangement = Arrangement.spacedBy(spaceSmall, Alignment.CenterHorizontally),\n            verticalArrangement = Arrangement.spacedBy(spaceSmall, Alignment.CenterVertically),\n            contentPadding = contentPadding\n        ) {\n            items(items = keyList) { key ->\n                WordleKeyboardKey(\n                    key = key,\n                    disabled = key in disabledKeys,\n                    onKeyClick = { onKeyClick(key) },\n                    modifier = Modifier.size(35.dp)\n                )\n            }\n        }\n    }\n}\n\n/**\n * A single key on a keyboard.\n *\n * @param modifier the modifier to apply to the key\n * @param key the character displayed on the key\n * @param disabled whether the key should be disabled and not clickable\n * @param onKeyClick a callback to be invoked when the key is clicked\n */\n@Composable\n@OptIn(ExperimentalMaterial3Api::class)\ninternal fun WordleKeyboardKey(\n    modifier: Modifier = Modifier,\n    key: Char,\n    disabled: Boolean,\n    onKeyClick: () -> Unit\n) {\n    Card(\n        shape = MaterialTheme.shapes.medium,\n        modifier = modifier\n            .size(35.dp)\n            .testTag(WordleKeyBoardTestingTags.KEY),\n        onClick = onKeyClick,\n        enabled = !disabled\n    ) {\n        Box(\n            modifier = Modifier.fillMaxSize(),\n            contentAlignment = Alignment.Center\n        ) {\n            Text(\n                text = key.toString(),\n                style = MaterialTheme.typography.titleMedium,\n                textAlign = TextAlign.Center\n            )\n        }\n    }\n}\n\n@VisibleForTesting\ninternal object WordleKeyBoardTestingTags {\n    const val KEY = \"WordleKeyBoardKey\"\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class, ExperimentalLayoutApi::class)\nprivate fun WordKeyBoardPreview() {\n    val configuration = LocalConfiguration.current\n    val screenHeight = configuration.screenHeightDp.dp\n    val screenWidth = configuration.screenWidthDp.dp\n    val windowWidthClass = WindowSizeClass.calculateFromSize(DpSize(screenWidth, screenHeight))\n\n    NewQuizTheme {\n        Surface(\n            modifier = Modifier.fillMaxSize()\n        ) {\n            Box(\n                modifier = Modifier.fillMaxSize(),\n                contentAlignment = Alignment.Center\n            ) {\n                WordleKeyBoard(\n                    keys = \"QWERTYUIOPASDFGHJKLZXCVBNM\".toCharArray(),\n                    disabledKeys = \"AKDTVMGUI\".toSet(),\n                    onKeyClick = {},\n                    wordleQuizType = WordleQuizType.TEXT,\n                    windowWidthSizeClass = windowWidthClass.widthSizeClass\n                )\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class, ExperimentalLayoutApi::class)\nprivate fun WordKeyBoardNumbersPreview() {\n    val allNumbers = \"1234567890\"\n\n    val configuration = LocalConfiguration.current\n    val screenHeight = configuration.screenHeightDp.dp\n    val screenWidth = configuration.screenWidthDp.dp\n    val windowWidthClass = WindowSizeClass.calculateFromSize(DpSize(screenWidth, screenHeight))\n\n    NewQuizTheme {\n        Surface(\n            modifier = Modifier.fillMaxSize()\n        ) {\n            Box(\n                modifier = Modifier.fillMaxSize(),\n                contentAlignment = Alignment.Center\n            ) {\n                WordleKeyBoard(\n                    keys = allNumbers.toList().toCharArray(),\n                    disabledKeys = \"136\".toSet(),\n                    onKeyClick = {},\n                    wordleQuizType = WordleQuizType.NUMBER,\n                    windowWidthSizeClass = windowWidthClass.widthSizeClass\n                )\n            }\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3WindowSizeClassApi::class, ExperimentalLayoutApi::class)\nprivate fun WordKeyBoardMathFormulaPreview() {\n    val allNumbers = \"1234567890+-*/=\"\n\n    val configuration = LocalConfiguration.current\n    val screenHeight = configuration.screenHeightDp.dp\n    val screenWidth = configuration.screenWidthDp.dp\n    val windowWidthClass = WindowSizeClass.calculateFromSize(DpSize(screenWidth, screenHeight))\n\n    NewQuizTheme {\n        Surface(\n            modifier = Modifier.fillMaxSize()\n        ) {\n            Box(\n                modifier = Modifier.fillMaxSize(),\n                contentAlignment = Alignment.Center\n            ) {\n                WordleKeyBoard(\n                    keys = allNumbers.toList().toCharArray(),\n                    disabledKeys = \"136\".toSet(),\n                    onKeyClick = {},\n                    wordleQuizType = WordleQuizType.MATH_FORMULA,\n                    windowWidthSizeClass = windowWidthClass.widthSizeClass\n                )\n            }\n        }\n    }\n}"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/components/WordleRowComponent.kt",
    "content": "package com.infinitepower.newquiz.wordle.components\n\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.animation.AnimatedVisibility\nimport androidx.compose.animation.animateColor\nimport androidx.compose.animation.core.MutableTransitionState\nimport androidx.compose.animation.core.animateDp\nimport androidx.compose.animation.core.tween\nimport androidx.compose.animation.core.updateTransition\nimport androidx.compose.animation.fadeIn\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.material3.Badge\nimport androidx.compose.material3.BadgedBox\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.ReadOnlyComposable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.platform.LocalInspectionMode\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.compose.ui.unit.dp\nimport com.infinitepower.newquiz.core.theme.CustomColor\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.theme.extendedColors\nimport com.infinitepower.newquiz.core.theme.spacing\nimport com.infinitepower.newquiz.model.wordle.WordleChar\nimport com.infinitepower.newquiz.model.wordle.WordleItem\nimport com.infinitepower.newquiz.model.wordle.WordleRowItem\nimport com.infinitepower.newquiz.core.R as CoreR\n\n/**\n * This composable function creates a row of wordle items.\n * Each item represents a character in the provided [wordleRowItem].\n *\n * @param modifier Modifier to modify the row component\n * @param word wordle word\n * @param wordleRowItem An object containing all the items in the row\n * @param onItemClick called when an item in the row is clicked\n */\n@Composable\ninternal fun WordleRowComponent(\n    modifier: Modifier = Modifier,\n    word: String,\n    wordleRowItem: WordleRowItem,\n    enabled: Boolean = true,\n    isColorBlindEnabled: Boolean = false,\n    isLetterHintsEnabled: Boolean = false,\n    animationEnabled: Boolean = !LocalInspectionMode.current,\n    onItemClick: (index: Int) -> Unit\n) {\n    WordleRowContainer(\n        modifier = modifier,\n        word = word,\n        wordleRowItem = wordleRowItem,\n        animationEnabled = animationEnabled\n    ) { item, index, wordCharCount, itemCharCount ->\n        WordleComponent(\n            item = item,\n            enabled = enabled,\n            isColorBlindEnabled = isColorBlindEnabled,\n            onClick = { onItemClick(index) },\n            charCount = wordCharCount,\n            isLetterHintsEnabled = isLetterHintsEnabled && wordCharCount != itemCharCount\n        )\n    }\n}\n\n@Composable\nprivate fun WordleRowContainer(\n    modifier: Modifier = Modifier,\n    word: String,\n    wordleRowItem: WordleRowItem,\n    animationEnabled: Boolean = true,\n    wordleComponentContent: @Composable (\n        item: WordleItem,\n        index: Int,\n        wordCharCount: Int,\n        itemCharCount: Int\n    ) -> Unit\n) {\n    val presentItems = wordleRowItem\n        .items\n        .filterIsInstance<WordleItem.Present>()\n\n    val state = remember {\n        MutableTransitionState(!animationEnabled).apply {\n            // Start the animation immediately.\n            targetState = true\n        }\n    }\n\n    Row(\n        horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.small),\n        modifier = modifier\n    ) {\n        wordleRowItem.items.forEachIndexed { index, item ->\n            val wordCharCount = word.count { wordChar ->\n                wordChar == item.char.value && item is WordleItem.Present\n            }\n\n            val itemCharCount = presentItems.count { presentItem ->\n                presentItem.char == item.char && item is WordleItem.Present\n            }\n\n            if (animationEnabled) {\n                AnimatedVisibility(\n                    visibleState = state,\n                    enter = fadeIn(\n                        animationSpec = tween(\n                            durationMillis = 150,\n                            delayMillis = 150 * index\n                        )\n                    )\n                ) {\n                    wordleComponentContent(item, index, wordCharCount, itemCharCount)\n                }\n            } else {\n                wordleComponentContent(item, index, wordCharCount, itemCharCount)\n            }\n        }\n    }\n}\n\n@Composable\ninternal fun WordleComponent(\n    modifier: Modifier = Modifier,\n    item: WordleItem,\n    enabled: Boolean = true,\n    isColorBlindEnabled: Boolean = false,\n    isLetterHintsEnabled: Boolean = false,\n    charCount: Int = 0,\n    onClick: () -> Unit\n) {\n    if (charCount > 1 && isLetterHintsEnabled) {\n        BadgedBox(\n            badge = {\n                Badge {\n                    val badgeNumber = charCount.toString()\n                    Text(\n                        text = badgeNumber,\n                        modifier = Modifier.semantics {\n                            contentDescription = \"Hint: $badgeNumber letter(s) in the word\"\n                        }\n                    )\n                }\n            }\n        ) {\n            WordleComponentImpl(\n                modifier = modifier,\n                item = item,\n                enabled = enabled,\n                isColorBlindEnabled = isColorBlindEnabled,\n                onClick = onClick\n            )\n        }\n    } else {\n        WordleComponentImpl(\n            modifier = modifier,\n            item = item,\n            enabled = enabled,\n            isColorBlindEnabled = isColorBlindEnabled,\n            onClick = onClick\n        )\n    }\n}\n\n@Composable\nprivate fun WordleComponentImpl(\n    modifier: Modifier = Modifier,\n    item: WordleItem,\n    enabled: Boolean = true,\n    isColorBlindEnabled: Boolean = false,\n    onClick: () -> Unit\n) {\n    val transition = updateTransition(\n        targetState = item,\n        label = \"Wordle Component\"\n    )\n\n    val backgroundColorAnimated by transition.animateColor(\n        label = \"Background Color\"\n    ) {\n        getItemRowBackgroundColor(\n            item = it,\n            isColorBlindEnabled = isColorBlindEnabled\n        )\n    }\n\n    val textColorAnimated by transition.animateColor(\n        label = \"Text Color\"\n    ) {\n        getItemRowTextColor(\n            item = it,\n            isColorBlindEnabled = isColorBlindEnabled\n        )\n    }\n\n    val elevationAnimated by transition.animateDp(\n        label = \"Elevation\"\n    ) {\n        if (it.char.isEmpty()) 0.dp else 8.dp\n    }\n\n    val stroke = if (item.char.isEmpty()) {\n        BorderStroke(1.dp, MaterialTheme.colorScheme.outline)\n    } else {\n        null\n    }\n\n    val itemDescription = item.getItemDescription()\n\n    Surface(\n        shape = MaterialTheme.shapes.medium,\n        modifier = modifier\n            .size(50.dp)\n            .testTag(WordleRowComponentTestingTags.WORDLE_COMPONENT_SURFACE)\n            .semantics {\n                contentDescription = itemDescription\n            },\n        color = backgroundColorAnimated,\n        tonalElevation = elevationAnimated,\n        border = stroke,\n        onClick = onClick,\n        enabled = item is WordleItem.None && enabled\n    ) {\n        Box(\n            modifier = Modifier.fillMaxSize(),\n            contentAlignment = Alignment.Center\n        ) {\n            Text(\n                text = item.char.toString(),\n                style = MaterialTheme.typography.titleLarge,\n                textAlign = TextAlign.Center,\n                color = textColorAnimated\n            )\n        }\n    }\n}\n\n@Composable\n@ReadOnlyComposable\ninternal fun getItemRowBackgroundColor(\n    item: WordleItem,\n    isColorBlindEnabled: Boolean\n): Color {\n    return when (item) {\n        is WordleItem.Correct -> MaterialTheme.extendedColors.getColorByKey(\n            key = if (isColorBlindEnabled) {\n                CustomColor.Key.Blue\n            } else {\n                CustomColor.Key.Green\n            }\n        )\n\n        is WordleItem.Present -> MaterialTheme.extendedColors.getColorByKey(\n            key = if (isColorBlindEnabled) {\n                CustomColor.Key.Red\n            } else {\n                CustomColor.Key.Yellow\n            }\n        )\n\n        else -> MaterialTheme.colorScheme.surface\n    }\n}\n\n@Composable\n@ReadOnlyComposable\ninternal fun getItemRowTextColor(\n    item: WordleItem,\n    isColorBlindEnabled: Boolean\n): Color {\n    return when (item) {\n        is WordleItem.Correct -> MaterialTheme.extendedColors.getOnColorByKey(\n            key = if (isColorBlindEnabled) {\n                CustomColor.Key.Blue\n            } else {\n                CustomColor.Key.Green\n            }\n        )\n\n        is WordleItem.Present -> MaterialTheme.extendedColors.getOnColorByKey(\n            key = if (isColorBlindEnabled) {\n                CustomColor.Key.Red\n            } else {\n                CustomColor.Key.Yellow\n            }\n        )\n\n        else -> MaterialTheme.colorScheme.onSurface\n    }\n}\n\n@Composable\n@ReadOnlyComposable\nprivate fun WordleItem.getItemDescription() = when (this) {\n    is WordleItem.Empty -> stringResource(id = CoreR.string.item_empty)\n    is WordleItem.None -> stringResource(id = CoreR.string.item_i_none, this.char)\n    is WordleItem.Present -> stringResource(id = CoreR.string.item_i_present, this.char)\n    is WordleItem.Correct -> stringResource(id = CoreR.string.item_i_correct, this.char)\n}\n\n@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)\ninternal object WordleRowComponentTestingTags {\n    const val WORDLE_COMPONENT_SURFACE = \"WORDLE_COMPONENT_SURFACE\"\n}\n\n@Composable\n@PreviewLightDark\nprivate fun WordleComponentPreview() {\n    val item = WordleItem.fromChar('A')\n\n    NewQuizTheme {\n        Surface {\n            WordleComponent(\n                modifier = Modifier.padding(16.dp),\n                item = item,\n                onClick = {}\n            )\n        }\n    }\n}\n\n@Composable\n@PreviewLightDark\nprivate fun WordleRowComponentPreview() {\n    val item = WordleRowItem(\n        items = listOf(\n            WordleItem.None(char = WordleChar('A')),\n            WordleItem.Present(char = WordleChar('B')),\n            WordleItem.Correct(char = WordleChar('C')),\n            WordleItem.Present(char = WordleChar('U')),\n            WordleItem.Empty,\n        )\n    )\n\n    NewQuizTheme {\n        Surface {\n            WordleRowComponent(\n                modifier = Modifier.padding(16.dp),\n                word = \"BUCZB\",\n                wordleRowItem = item,\n                onItemClick = {},\n                isLetterHintsEnabled = true,\n                animationEnabled = false\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/list/WordleListScreen.kt",
    "content": "package com.infinitepower.newquiz.wordle.list\n\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.PreviewLightDark\nimport androidx.hilt.navigation.compose.hiltViewModel\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.LocalAnalyticsHelper\nimport com.infinitepower.newquiz.core.theme.NewQuizTheme\nimport com.infinitepower.newquiz.core.ui.home.HomeLazyColumn\nimport com.infinitepower.newquiz.core.ui.home.homeCategoriesItems\nimport com.infinitepower.newquiz.core.ui.home_card.components.PlayRandomQuizCard\nimport com.infinitepower.newquiz.data.local.wordle.WordleCategories\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport com.infinitepower.newquiz.wordle.destinations.WordleScreenDestination\nimport com.ramcosta.composedestinations.annotation.Destination\nimport com.ramcosta.composedestinations.navigation.DestinationsNavigator\nimport com.infinitepower.newquiz.core.R as CoreR\n\n@Composable\n@Destination\n@OptIn(ExperimentalMaterial3Api::class)\nfun WordleListScreen(\n    navigator: DestinationsNavigator,\n    homeViewModel: WordleListScreenViewModel = hiltViewModel()\n) {\n    val uiState by homeViewModel.uiState.collectAsStateWithLifecycle()\n\n    WordleListScreenImpl(\n        uiState = uiState,\n        navigateToWordleQuiz = { wordleQuizType ->\n            navigator.navigate(WordleScreenDestination(quizType = wordleQuizType))\n        }\n    )\n}\n\n@Composable\n@ExperimentalMaterial3Api\nprivate fun WordleListScreenImpl(\n    uiState: WordleListUiState,\n    navigateToWordleQuiz: (wordleQuizType: WordleQuizType) -> Unit\n) {\n    val analyticsHelper = LocalAnalyticsHelper.current\n    var seeAllCategories by remember { mutableStateOf(false) }\n\n    HomeLazyColumn {\n        item {\n            PlayRandomQuizCard(\n                modifier = Modifier.fillParentMaxWidth(),\n                title = stringResource(id = CoreR.string.quiz_with_random_categories),\n                buttonTitle = stringResource(id = CoreR.string.random_quiz),\n                containerMainColor = MaterialTheme.colorScheme.primary,\n                onClick = {\n                    val randomCategory = WordleCategories.random(uiState.internetConnectionAvailable)\n                    navigateToWordleQuiz(randomCategory.wordleQuizType)\n                },\n                enabled = uiState.internetConnectionAvailable,\n            )\n        }\n\n        item {\n            Text(\n                text = stringResource(id = CoreR.string.categories),\n                style = MaterialTheme.typography.bodyMedium\n            )\n        }\n\n        homeCategoriesItems(\n            seeAllCategories = seeAllCategories,\n            recentCategories = uiState.homeCategories.recentCategories,\n            otherCategories = uiState.homeCategories.otherCategories,\n            isInternetAvailable = uiState.internetConnectionAvailable,\n            onCategoryClick = { category ->\n                analyticsHelper.logEvent(\n                    AnalyticsEvent.CategoryClicked(\n                        game = AnalyticsEvent.Game.WORDLE,\n                        categoryId = category.id\n                    )\n                )\n\n                navigateToWordleQuiz(category.wordleQuizType)\n            },\n            onSeeAllCategoriesClick = { seeAllCategories = !seeAllCategories },\n            showConnectionInfo = uiState.showCategoryConnectionInfo\n        )\n    }\n}\n\n@Composable\n@PreviewLightDark\n@OptIn(ExperimentalMaterial3Api::class)\nprivate fun WordleListScreenPreview() {\n    NewQuizTheme {\n        Surface {\n            WordleListScreenImpl(\n                uiState = WordleListUiState(),\n                navigateToWordleQuiz = {}\n            )\n        }\n    }\n}"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/list/WordleListScreenViewModel.kt",
    "content": "package com.infinitepower.newquiz.wordle.list\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.infinitepower.newquiz.core.network.NetworkStatusTracker\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.stateIn\nimport javax.inject.Inject\n\n@HiltViewModel\nclass WordleListScreenViewModel @Inject constructor(\n    recentCategoriesRepository: RecentCategoriesRepository,\n    networkStatusTracker: NetworkStatusTracker\n) : ViewModel() {\n    val uiState = combine(\n        recentCategoriesRepository.getWordleCategories(\n            isInternetAvailable = networkStatusTracker.isCurrentlyConnected()\n        ),\n        recentCategoriesRepository.getShowCategoryConnectionInfoFlow()\n    ) { recentCategories, showCategoryConnectionInfo ->\n        WordleListUiState(\n            homeCategories = recentCategories,\n            internetConnectionAvailable = networkStatusTracker.isCurrentlyConnected(),\n            showCategoryConnectionInfo = showCategoryConnectionInfo\n        )\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.WhileSubscribed(5_000),\n        initialValue = WordleListUiState()\n    )\n}"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/list/WordleListUiState.kt",
    "content": "package com.infinitepower.newquiz.wordle.list\n\nimport androidx.annotation.Keep\nimport com.infinitepower.newquiz.domain.repository.home.HomeCategories\nimport com.infinitepower.newquiz.domain.repository.home.emptyHomeCategories\nimport com.infinitepower.newquiz.model.category.ShowCategoryConnectionInfo\nimport com.infinitepower.newquiz.model.wordle.WordleCategory\n\n@Keep\ndata class WordleListUiState(\n    val homeCategories: HomeCategories<WordleCategory> = emptyHomeCategories(),\n    val internetConnectionAvailable: Boolean = true,\n    val showCategoryConnectionInfo: ShowCategoryConnectionInfo = ShowCategoryConnectionInfo.NONE\n)\n"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/util/InvalidWordErrorUiText.kt",
    "content": "package com.infinitepower.newquiz.wordle.util\n\nimport com.infinitepower.newquiz.data.repository.wordle.InvalidWordError\nimport com.infinitepower.newquiz.model.UiText\nimport com.infinitepower.newquiz.core.R as CoreR\n\nfun InvalidWordError.asUiText(): UiText {\n    return when (this) {\n        InvalidWordError.Empty -> UiText.StringResource(CoreR.string.empty_word)\n        InvalidWordError.NotOnlyLetters -> UiText.StringResource(CoreR.string.word_not_only_letters_error)\n        InvalidWordError.NotOnlyDigits -> UiText.StringResource(CoreR.string.word_not_only_digits_error)\n        InvalidWordError.InvalidMathFormula -> UiText.StringResource(CoreR.string.word_invalid_math_formula_error)\n    }\n}\n"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/util/word/WordUtil.kt",
    "content": "package com.infinitepower.newquiz.wordle.util.word\n\nimport com.infinitepower.newquiz.model.wordle.WordleItem\n\n/**\n * Verifies the input [WordleItem] list with [originalWord]\n *\n * This function will map all [WordleItem] list and return the corresponded verified item\n * that can be [WordleItem.None], [WordleItem.Present] and [WordleItem.Correct].\n *\n * ### Mapping List\n * If the item is in the word and in the correct spot returns [WordleItem.Correct].\n * If the item is in the word and not in the correct spot returns [WordleItem.Present].\n * If the item is not in the word returns [WordleItem.None].\n *\n *\n * @param originalWord word to verify items\n * @return verified [WordleItem] items\n * @see [WordleItem]\n * @author João Manaia\n * @since 1.0.0\n */\ninternal infix fun List<WordleItem>.verifyFromWord(originalWord: String): List<WordleItem> {\n    val removeChars = originalWord.filterIndexed { index, char ->\n        getOrNull(index)?.char?.value != char\n    }.toMutableList()\n\n    val newList = mapIndexed { index, wordleItem ->\n        val char = wordleItem.char\n\n        val charCorrect = originalWord[index] == char.value\n        if (charCorrect) return@mapIndexed WordleItem.Correct(char)\n\n        val charPresent = char.value in originalWord && removeChars.remove(wordleItem.char.value)\n        if (charPresent) return@mapIndexed WordleItem.Present(char)\n\n        WordleItem.None(char, true)\n    }\n\n    return newList\n}\n\n/**\n * Returns `true` if all elements in [lastRevealedHints] are present in this list.\n *\n * @param lastRevealedHints the list of elements to check against this list\n * @return true if all elements in [lastRevealedHints] are present in this list\n */\ninternal infix fun List<WordleItem>.containsAllLastRevealedHints(\n    lastRevealedHints: List<WordleItem>\n): Boolean = lastRevealedHints.all { item -> // Check if all elements in lastRevealedHints are present in this list\n    // Check if there is at least one element in this list that has the same character as `item`\n    this.any { it.char == item.char }\n}\n\n/**\n * Gets keys to disable with list of [WordleItem].\n *\n * If the item is [WordleItem.None] will disable the item key.\n * If the list contains one item [WordleItem.Correct] or [WordleItem.Present] and one item [WordleItem.None] will\n * not disable the key.\n *\n * @return keys disabled\n * @see [WordleItem]\n * @author João Manaia\n * @since 1.0.0\n */\ninternal fun List<WordleItem>.getKeysDisabled(): Set<Char> {\n    return filter { item ->\n        val sameItemCount = count { char ->\n            (char is WordleItem.Correct || char is WordleItem.Present) && char.char == item.char\n        }\n        item is WordleItem.None && sameItemCount == 0\n    }.mapNotNull { item ->\n        val char = item.char.value\n        if (char == ' ') null else char\n    }.toSet()\n}"
  },
  {
    "path": "wordle/src/main/java/com/infinitepower/newquiz/wordle/util/worker/WordleEndGameWorker.kt",
    "content": "package com.infinitepower.newquiz.wordle.util.worker\n\nimport android.content.Context\nimport androidx.hilt.work.HiltWorker\nimport androidx.work.CoroutineWorker\nimport androidx.work.WorkManager\nimport androidx.work.WorkerParameters\nimport com.infinitepower.newquiz.core.analytics.AnalyticsEvent\nimport com.infinitepower.newquiz.core.analytics.AnalyticsHelper\nimport com.infinitepower.newquiz.core.user_services.UserService\nimport com.infinitepower.newquiz.data.worker.UpdateGlobalEventDataWorker\nimport com.infinitepower.newquiz.domain.repository.home.RecentCategoriesRepository\nimport com.infinitepower.newquiz.model.global_event.GameEvent\nimport com.infinitepower.newquiz.model.wordle.WordleQuizType\nimport dagger.assisted.Assisted\nimport dagger.assisted.AssistedInject\n\n@HiltWorker\nclass WordleEndGameWorker @AssistedInject constructor(\n    @Assisted appContext: Context,\n    @Assisted workerParams: WorkerParameters,\n    private val workManager: WorkManager,\n    private val recentCategoriesRepository: RecentCategoriesRepository,\n    private val analyticsHelper: AnalyticsHelper,\n    private val userService: UserService\n) : CoroutineWorker(appContext, workerParams) {\n\n    companion object {\n        const val INPUT_WORD = \"word\"\n        const val INPUT_ROW_LIMIT = \"row_limit\"\n        const val INPUT_CURRENT_ROW_POSITION = \"current_row_position\"\n        const val INPUT_IS_LAST_ROW_CORRECT = \"is_last_row_correct\"\n        const val INPUT_QUIZ_TYPE = \"quiz_type\"\n        const val INPUT_MAZE_TEM_ID = \"maze_item_id\"\n    }\n\n    override suspend fun doWork(): Result {\n        val word = inputData.getString(INPUT_WORD) ?: return Result.failure()\n        val rowLimit = inputData.getInt(INPUT_ROW_LIMIT, 0)\n        val currentRowPosition = inputData.getInt(INPUT_CURRENT_ROW_POSITION, 0)\n        val isLastRowCorrect = inputData.getBoolean(INPUT_IS_LAST_ROW_CORRECT, false)\n        val quizTypeName = inputData.getString(INPUT_QUIZ_TYPE) ?: WordleQuizType.TEXT.name\n        val mazeItemId = inputData.getString(INPUT_MAZE_TEM_ID)\n\n        recentCategoriesRepository.addWordleCategory(quizTypeName)\n\n        if (isLastRowCorrect) {\n            UpdateGlobalEventDataWorker.enqueueWork(\n                workManager = workManager,\n                GameEvent.Wordle.GetWordCorrect\n            )\n        }\n\n        analyticsHelper.logEvent(\n            AnalyticsEvent.WordleGameEnd(\n                wordLength = word.length,\n                maxRows = rowLimit,\n                lastRow = currentRowPosition,\n                lastRowCorrect = isLastRowCorrect,\n                category = quizTypeName,\n                mazeItemId = mazeItemId?.toIntOrNull()\n            )\n        )\n\n        if (mazeItemId != null) {\n            analyticsHelper.logEvent(AnalyticsEvent.MazeItemPlayed(isLastRowCorrect))\n        }\n\n        if (isLastRowCorrect) {\n            userService.saveWordleGame(\n                wordLength = word.length.toUInt(),\n                rowsUsed = currentRowPosition.toUInt(),\n                maxRows = rowLimit,\n                categoryId = quizTypeName,\n                generateXp = true\n            )\n        }\n\n        return Result.success()\n    }\n}"
  },
  {
    "path": "wordle/src/test/java/com/infinitepower/newquiz/wordle/util/word/WordUtilTest.kt",
    "content": "package com.infinitepower.newquiz.wordle.util.word\n\nimport com.google.common.truth.Truth.assertThat\nimport com.infinitepower.newquiz.model.wordle.WordleChar\nimport com.infinitepower.newquiz.model.wordle.WordleItem\nimport kotlin.test.Test\n\ninternal class WordUtilTest {\n\n    @Test\n    fun `verify from word`() {\n        val originalWord = \"DBCKE\"\n\n        val items = listOf(\n            WordleItem.fromChar('A'),\n            WordleItem.fromChar('B'),\n            WordleItem.fromChar('C'),\n            WordleItem.fromChar('D'),\n            WordleItem.fromChar('E'),\n        )\n\n        val verifiedItems = items verifyFromWord originalWord\n\n        val expectedItems = listOf(\n            WordleItem.None(WordleChar('A'), true),\n            WordleItem.Correct(WordleChar('B')),\n            WordleItem.Correct(WordleChar('C')),\n            WordleItem.Present(WordleChar('D')),\n            WordleItem.Correct(WordleChar('E')),\n        )\n        assertThat(verifiedItems).containsExactlyElementsIn(expectedItems)\n    }\n\n    @Test\n    fun `verify from word, present chars must be same size as original word`() {\n        val originalWord = \"WORDLE\"\n\n        val items = listOf(\n            WordleItem.fromChar('E'),\n            WordleItem.fromChar('E'),\n            WordleItem.fromChar('E'),\n            WordleItem.fromChar('E'),\n            WordleItem.fromChar('E'),\n            WordleItem.fromChar('E'),\n        )\n\n        val verifiedItems = items verifyFromWord originalWord\n\n        val expectedItems = listOf(\n            WordleItem.None(WordleChar('E'), true),\n            WordleItem.None(WordleChar('E'), true),\n            WordleItem.None(WordleChar('E'), true),\n            WordleItem.None(WordleChar('E'), true),\n            WordleItem.None(WordleChar('E'), true),\n            WordleItem.Correct(WordleChar('E')),\n        )\n        assertThat(verifiedItems).containsExactlyElementsIn(expectedItems)\n    }\n\n    @Test\n    fun `get keys disabled test`() {\n        val items = listOf(\n            WordleItem.None(WordleChar('A')),\n            WordleItem.Present(WordleChar('B')),\n            WordleItem.Present(WordleChar('C')),\n            WordleItem.None(WordleChar('D')),\n            WordleItem.Empty,\n            WordleItem.Correct(WordleChar('F')),\n        )\n\n        val keysDisabled = items.getKeysDisabled()\n\n        val expectedItems = listOf('A', 'D')\n\n        assertThat(keysDisabled).containsExactlyElementsIn(expectedItems)\n    }\n\n    @Test\n    fun `get keys disabled test, multiple same char`() {\n        // Because there are one correct item, we cannot disabled that key\n        val items = listOf(\n            WordleItem.None(WordleChar('F')),\n            WordleItem.Present(WordleChar('F')),\n            WordleItem.Correct(WordleChar('F')),\n            WordleItem.None(WordleChar('F')),\n            WordleItem.Present(WordleChar('F')),\n            WordleItem.Present(WordleChar('F')),\n        )\n\n        val keysDisabled = items.getKeysDisabled()\n        assertThat(keysDisabled).isEmpty()\n\n        val items2 = listOf(\n            WordleItem.None(WordleChar('F')),\n            WordleItem.None(WordleChar('F')),\n            WordleItem.None(WordleChar('F')),\n            WordleItem.None(WordleChar('F')),\n            WordleItem.None(WordleChar('F')),\n        )\n\n        val keysDisabled2 = items2.getKeysDisabled()\n\n        val expectedItems2 = listOf('F')\n        assertThat(keysDisabled2).containsExactlyElementsIn(expectedItems2)\n    }\n\n    @Test\n    fun `wordle list item contains all last revealed hints, returns true`() {\n        val items = listOf(\n            WordleItem.Present(WordleChar('A')),\n            WordleItem.None(WordleChar('B')),\n            WordleItem.None(WordleChar('B')),\n            WordleItem.Correct(WordleChar('A')),\n        )\n\n        val lastRevealedHints = listOf(\n            WordleItem.Present(WordleChar('A')),\n        )\n\n        val containsAllLastRevealedHints = items containsAllLastRevealedHints lastRevealedHints\n        assertThat(containsAllLastRevealedHints).isTrue()\n\n        val items2 = listOf(\n            WordleItem.Correct(WordleChar('E')),\n            WordleItem.None(WordleChar('F')),\n            WordleItem.None(WordleChar('N')),\n            WordleItem.None(WordleChar('F')),\n            WordleItem.Present(WordleChar('I')),\n        )\n\n        val lastRevealedHints2 = listOf(\n            WordleItem.Present(WordleChar('E')),\n            WordleItem.Present(WordleChar('I')),\n        )\n\n        val containsAllLastRevealedHints2 = items2 containsAllLastRevealedHints lastRevealedHints2\n        assertThat(containsAllLastRevealedHints2).isTrue()\n    }\n\n    @Test\n    fun `wordle list item not contains all last revealed hints, returns false`() {\n        val items = listOf(\n            WordleItem.Present(WordleChar('A')),\n            WordleItem.None(WordleChar('B')),\n            WordleItem.None(WordleChar('B')),\n            WordleItem.Correct(WordleChar('A')),\n        )\n\n        val lastRevealedHints = listOf(\n            WordleItem.Present(WordleChar('A')),\n            WordleItem.Correct(WordleChar('K')),\n            WordleItem.Present(WordleChar('Z')),\n        )\n\n        val containsAllLastRevealedHints = items containsAllLastRevealedHints lastRevealedHints\n        assertThat(containsAllLastRevealedHints).isFalse()\n    }\n\n    @Test\n    fun `wordle list item contains all empty list last revealed hints, returns true`() {\n        val items = listOf(\n            WordleItem.Present(WordleChar('A')),\n            WordleItem.None(WordleChar('B')),\n            WordleItem.None(WordleChar('B')),\n            WordleItem.Correct(WordleChar('A')),\n        )\n\n        val lastRevealedHints = emptyList<WordleItem>()\n\n        val containsAllLastRevealedHints = items containsAllLastRevealedHints lastRevealedHints\n        assertThat(containsAllLastRevealedHints).isTrue()\n    }\n}"
  }
]