[
  {
    "path": ".github/.java-version",
    "content": "24\n"
  },
  {
    "path": ".github/renovate.json5",
    "content": "{\n  $schema: 'https://docs.renovatebot.com/renovate-schema.json',\n  extends: [\n    'config:recommended',\n  ],\n  reviewers: ['kevincianfarini'],\n  ignorePresets: [\n    // Ensure we get the latest version and are not pinned to old versions.\n    'workarounds:javaLTSVersions',\n  ],\n  customManagers: [\n    // Update .java-version file with the latest JDK version.\n    {\n      customType: 'regex',\n      fileMatch: [\n        '\\\\.java-version$',\n      ],\n      matchStrings: [\n        '(?<currentValue>.*)\\\\n',\n      ],\n      datasourceTemplate: 'java-version',\n      depNameTemplate: 'java',\n      // Only write the major version.\n      extractVersionTemplate: '^(?<version>\\\\d+)',\n    },\n  ],\n}"
  },
  {
    "path": ".github/workflows/gradle-wrapper.yaml",
    "content": "name: gradle-wrapper\n\non:\n  pull_request:\n    paths:\n      - 'gradlew'\n      - 'gradlew.bat'\n      - 'gradle/wrapper/**'\n\njobs:\n  validate:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: gradle/wrapper-validation-action@v3"
  },
  {
    "path": ".github/workflows/release.yaml",
    "content": "name: release\n\non:\n  push:\n    branches:\n      - 'trunk'\n\nenv:\n  GRADLE_OPTS: \"-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false\"\n\njobs:\n  publish:\n    runs-on: macos-latest\n\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-java@v4\n        with:\n          distribution: 'zulu'\n          java-version-file: .github/.java-version\n\n      - name: Build and publish artifacts\n        env:\n          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALUSERNAME }}\n          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALPASSWORD }}\n          ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGINMEMORYKEY }}\n        run: ./gradlew dokkaGenerate publish\n\n      - name: Deploy docs to website\n        uses: JamesIves/github-pages-deploy-action@releases/v3\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          BRANCH: site\n          FOLDER: build/dokka/html\n          TARGET_FOLDER: docs/0.x/\n          CLEAN: true"
  },
  {
    "path": ".github/workflows/test.yaml",
    "content": "name: test\n\non:\n  pull_request: {}\n  push:\n    branches:\n      - 'trunk'\n\nenv:\n  GRADLE_OPTS: \"-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false\"\n\njobs:\n  test:\n    strategy:\n      matrix:\n        os: [macOS-latest, windows-latest, ubuntu-latest]\n        include:\n          - os: macOS-latest\n            job: iosSimulatorArm64Test iosX64Test macosArm64Test macosX64Test tvosSimulatorArm64Test tvosX64Test watchosSimulatorArm64Test watchosX64Test\n          - os: windows-latest\n            job: mingwX64Test\n          - os: ubuntu-latest\n            job: jsTest jvmTest linuxX64Test wasmJsTest wasmWasiTest\n\n    runs-on: ${{matrix.os}}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-java@v4\n        with:\n          distribution: 'zulu'\n          java-version-file: .github/.java-version\n\n      - run: ./gradlew ${{matrix.job}}\n\n  kmp-missing-targets:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-java@v4\n        with:\n          distribution: 'zulu'\n          java-version-file: .github/.java-version\n      - run: ./gradlew kmpMissingTargets\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n.gradle\n**/build\nlocal.properties\n.kotlin"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 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": "ModuleDocumentation.md",
    "content": "# Module alchemist\n\nAlchemist allows you to manage physical quanities defined in the [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units). \nLike [Duration](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.time/-duration/), alchemist models its quanities as a value class with a single underlying `Long` value. \n\n### Type Safety\n\nAlchemist's main goal is to provide type safety for arithmetic on physical quanities. There's no need to pass loosely \ntyped `Long`, `Int`, or `Double` values around anymore! \n\n```kt\nval energy1 = 10.wattHours\nval energy2 = 10.millijoules\nprintln(energy1 + energy2) // OK: both are type Energy.\n\nval power = 10.watts\nprintln(energy1 + power) // Compiler Error! \n```\n\n### Scalar Arithmetic \n\nPhysical quantities expose scalar arithmetic like the following:\n\n```kt\nval power = 10.watts\nprintln(power / 2) // \"5W\"\nprintln(power * 100) // \"1kW\"\nprintln(-power) // \"-10W\"\n```\n\nand each quantity exposes typed arithmetic: \n\n```kt\nval first = 10.wattHours\nval second = 2.wattHours\nprintln(first + second) // \"12Wh\"\nprintln(first - second) // \"8Wh\"\nprintln(second - first) // \"-8Wh\"\nprintln(first / second) // \"5.0\"\n```\n\n### Physical Arithmetic \n\nPhysical quantities expose valid physical arithmetic defined in the International System of Units. For example: \n\n```kt\nval energy = 10.wattHours\nval power = 10.watts\nval duration: Duration = energy / power\nprintln(duration) // \"1h\"\n\nval length = 10.nanometers\nval force: Force = energy / length\nprintln(force) // \"3.6TN\"\n\nval velocity = length / 1.seconds\nprintln(velocity) // 10 nm/s\n\nval acceleration = velocity / 1.seconds\nprintln(acceleration) // 10 nm/s²\n```\n\n### Integration with the Kotlin Standard Library \n\nKotlin provides a measure of time, called [`Duration`](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.time/-duration/), \nin its standard library. Alchemist integrates with Duration for physical arithmetic rather than supplying its own \nimplementation of time. \n\n```kt\nval time: kotlin.time.Duration = 1.hours\nval energy = time * 1.watts\nprintln(energy) // \"1Wh\"\nprintln(energy / time) // \"1W\"\n\nval velocity = 1.meters / time\nprintln(velocity) // \"1 m/s\"\nprintln(velocity * time) // \"1m\"\n\nval acceleration = velocity / time\nprintln(velocity) // \"1 m/s²\nprintln(acceleration * time) // \"1 m/s\"\n```\n\n### Custom Quantity Unit Implementation \n\nEvery unit alchemist exposes is an interface, thus allowing you to implement non-standard or uncommonly used units: \n\n```kt\n// Alchemist does not provide horsepower out of the box. \nobject HorsePower : PowerUnit {\n    override val symbol: String get() = \"hp\"\n    override val microwattScale: Long get() = 745_699_871\n}\n\nval Int.horsepower: Power get() = this.toPower(HorsePower)\n\nprintln(1.horsepower) // \"745.7W\"\n```\n\nNote that alchemist does not provide certain units out of the box because they're impossible to represent without losing \nprecision. In the above example, 1 horsepower is actually 745,699,871.58μW.\n\n### Infinity \n\nWhile Long values can store huge numbers, sometimes they're not enough for the operations you're attempting to \nperform. Rather than silently over or underflowing during an arithmetic operation, Alchemist will clamp your quantities \nto a special infinite value. These special infinite values have some restrictions. \n\n```kt\n// This would overflow. \nval infiniteEnergy = 100.joules * Long.MAX_VALUE \nprintln(infiniteEnergy) // \"Infinity\"\n\n// Infinite values don't behave like other values. \nprintln(infiniteEnergy / Long.MAX_VALUE) // \"Infinity\"\nprintln(infiniteEnergy * 10) // \"Infinity\"\nprintln(-infiniteEnergy) // \"-Infinity\"\n\n// Some operations are invalid with infinite values. \nprintln(infiniteEnergy / infiniteEnergy) // Oops! This throws IllegalArgumentException. \nprintln(infiniteEnergy * -infiniteEnergy) // This throws, too. \n```\n\n### We're alchemists, not physicists! \n\nAlchemist is not built with physics engines in mind. Instead, Alchemist aims to provide type safety and reasonable levels of precision \nthat are sufficient for most business use cases. For example, Alchemist aims to help you model how much energy an entire country used \nfor a year with a reasonable level of precision, but it won't help you model how much energy the Sun produces in a day. \n\nAdditionally, Alchemist values type safety over modeling arbitrary quantities derived from SI base units. \nIntroducing that functionality involves too many compromises and would be better handled by a different library with different goals.\n\nIf you need to model things like how much energy your air conditioning used in a year, or how far your compact electric vehicle charged \nwith the electricity from your solar panels can drive, great! Alchemist might be a good fit for you. We hope you like it. \n"
  },
  {
    "path": "README.md",
    "content": "# Alchemist\n\nManage physical units. Inspired by kotlin.time.Duration. Alchemist allow type safe arithmetic between different \nphysical quantities defined in the [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units#).\n\n```kt\nval time: Duration = 10.seconds\nval length: Length = 10.kilometers\nval velocity: Velocity = length / time\nval acceleration: Acceleration = velocity / time\nval mass: Mass = 10.kilograms\nval force: Force = acceleration * mass\nval energy: Energy = force * length \nval power: Power = energy / time\nval area: Area = length * length \nval volume: Volume = length * length * length\n```\n\n## Download\n\n```toml\n[versions]\nalchemist = \"0.2.0\"\n\n[libraries]\nalchemist = { module = \"io.github.kevincianfarini.alchemist:alchemist\", version.ref = \"alchemist\" }\n```\n\n## Alchemist's Goals \n\n1. Model physical quantities as Kotlin value classes which wrap a single `Long` value. \n2. Provide logical arithmetic between different physical quantities, like `power = energy / time`. \n3. Allow for the implementation of custom units on physical quanities that Alchemist does not provide, such as horsepower as a unit of `Power` or Rankine degrees for `Temperature`. \n4. (In the future) Allow different order of magnitude precision and wider ranges of valid values. \n5. Easy extensibility for custom formulas, such as `energyₖ = ½ * mass * velocity²`.\n\n## Alchemist's Non-Goals\n\n1. Using generic or floating point values as the underlying storage mechanism. \n2. Representing arbitrary formulaic expressions. \n3. Providing as many formulaic conversions as possible out of the box, such as `energyₖ = ½ * mass * velocity²`.\n4. Infinitely precise values. \n5. Infinitely large ranges of valid values. \n\n## Platform Support \n\n| Platform             | Compiled | Tested in CI                                                                                       |\n|----------------------|----------|----------------------------------------------------------------------------------------------------|\n| androidNativeArm32   | ✅        | ❌                                                                                                  |\n| androidNativeArm64   | ✅        | ❌                                                                                                  |\n| androidNativeX64     | ✅        | ❌                                                                                                  |\n| androidNativeX86     | ✅        | ❌                                                                                                  |\n| iosArm64             | ✅        | ❌                                                                                                  |\n| iosSimulatorArm64    | ✅        | ✅                                                                                                  |\n| iosX64               | ✅        | ✅                                                                                                  |\n| js                   | ✅        | ✅                                                                                                  |\n| jvm                  | ✅        | ✅                                                                                                  |\n| linuxArm64           | ✅        | ❌ (Prohibited by [Tier 2](https://kotlinlang.org/docs/native-target-support.html#tier-2) support.) |\n| linuxX64             | ✅        | ✅                                                                                                  |\n| macosArm64           | ✅        | ✅                                                                                                  |\n| macosX64             | ✅        | ✅                                                                                                  |\n| mingwX64             | ✅        | ✅                                                                                                  |\n| tvosArm64            | ✅        | ❌                                                                                                  |\n| tvosSimulatorArm64   | ✅        | ✅                                                                                                  |\n| tvosX64              | ✅        | ✅                                                                                                  |\n| wasmJs               | ✅        | ✅                                                                                                  |\n| wasmWasi             | ✅        | ⚠️ (Some tests don't run because of [KT-60964](https://youtrack.jetbrains.com/issue/KT-60964).)    |\n| watchosArm32         | ✅        | ❌                                                                                                  |\n| watchosArm64         | ✅        | ❌                                                                                                  |\n| watchosDeviceArm64   | ✅        | ❌                                                                                                  |\n| watchosSimuatorArm64 | ✅        | ✅                                                                                                  |\n| watchosX64           | ✅        | ✅                                                                                                  |\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "import com.vanniktech.maven.publish.SonatypeHost\nimport org.jetbrains.kotlin.gradle.ExperimentalWasmDsl\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nplugins {\n    alias(libs.plugins.dokka)\n    alias(libs.plugins.kmpmt)\n    alias(libs.plugins.kotlin.multiplatform)\n    alias(libs.plugins.publish)\n}\n\n@OptIn(ExperimentalWasmDsl::class)\nkotlin {\n\n    explicitApi()\n\n    androidNativeArm32()\n    androidNativeArm64()\n    androidNativeX64()\n    androidNativeX86()\n    iosArm64()\n    iosSimulatorArm64()\n    iosX64()\n    js {\n        nodejs {\n            testTask {\n                useMocha {\n                    timeout = \"5s\"\n                }\n            }\n        }\n    }\n    jvm {\n        compilations.configureEach {\n            compilerOptions.options.apply {\n                jvmTarget = JvmTarget.JVM_1_8\n                freeCompilerArgs.add(\"-Xjvm-default=all\")\n            }\n        }\n    }\n    linuxArm64()\n    linuxX64()\n    macosArm64()\n    macosX64()\n    mingwX64()\n    tvosArm64()\n    tvosSimulatorArm64()\n    tvosX64()\n    wasmJs {\n        nodejs {\n            testTask {\n                useMocha {\n                    timeout = \"5s\"\n                }\n            }\n        }\n    }\n    wasmWasi { nodejs() }\n    watchosArm32()\n    watchosArm64()\n    watchosDeviceArm64()\n    watchosSimulatorArm64()\n    watchosX64()\n\n    sourceSets {\n\n        configureEach {\n            if (name.endsWith(\"Test\")) {\n                compilerOptions {\n                    freeCompilerArgs.add(\"-Xexpect-actual-classes\")\n                }\n            }\n        }\n\n        commonTest.dependencies {\n            implementation(libs.kotlin.test.core)\n        }\n    }\n}\n\ntasks.withType<AbstractTestTask> {\n    testLogging {\n        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL\n        showStandardStreams = true\n        showStackTraces = true\n    }\n}\n\nmavenPublishing {\n    publishToMavenCentral(host = SonatypeHost.S01, automaticRelease = true)\n    signAllPublications()\n}\n\ndokka {\n    val versionName = project.findProperty(\"VERSION_NAME\") as String\n    val version = if (versionName.contains(\"-SNAPSHOT\")) \"trunk\" else versionName\n    dokkaSourceSets.commonMain {\n        includes.from(\"ModuleDocumentation.md\")\n        sourceLink {\n            localDirectory.set(projectDir.resolve(\"src\"))\n            remoteUrl(\"https://github.com/kevincianfarini/alchemist/tree/${version}/src\")\n            remoteLineSuffix.set(\"#L\")\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\ndokka = \"2.0.0\"\nkmpmt = \"0.1.1\"\nkotlin = \"2.1.21\"\npublish = \"0.30.0\"\n\n[libraries]\nkotlin-test-core = { module = \"org.jetbrains.kotlin:kotlin-test\", version.ref = \"kotlin\" }\n\n[plugins]\ndokka = { id = \"org.jetbrains.dokka\", version.ref = \"dokka\" }\nkmpmt = { id = \"com.jakewharton.kmp-missing-targets\", version.ref = \"kmpmt\" }\nkotlin-multiplatform = { id = \"org.jetbrains.kotlin.multiplatform\", version.ref = \"kotlin\" }\npublish = { id = \"com.vanniktech.maven.publish\", version.ref = \"publish\" }\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.3-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "kotlin.code.style=official\n\nGROUP=io.github.kevincianfarini.alchemist\nPOM_ARTIFACT_ID=alchemist\nVERSION_NAME=0.2.1-SNAPSHOT\n\nPOM_NAME=Alchemist\nPOM_DESCRIPTION=Type safe management and arithmetic of physical units. Inspired by kotlin.time.Duration.\nPOM_INCEPTION_YEAR=2024\n\nPOM_URL=https://github.com/kevincianfarini/alchemist\nPOM_SCM_URL=https://github.com/kevincianfarini/alchemist\nPOM_SCM_CONNECTION=scm:git:git://github.com/kevincianfarini/alchemist.git\nPOM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/kevincianfarini/alchemist.git\n\nPOM_LICENCE_NAME=The Apache Software License, Version 2.0\nPOM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt\nPOM_LICENCE_DIST=repo\n\nPOM_DEVELOPER_ID=kevincianfarini\nPOM_DEVELOPER_NAME=Kevin Cianfarini\nPOM_DEVELOPER_URL=https://github.com/kevincianfarini\n\nSONATYPE_CONNECT_TIMEOUT_SECONDS=120\nSONATYPE_CLOSE_TIMEOUT_SECONDS=900\n\norg.jetbrains.dokka.experimental.gradle.pluginMode=V2EnabledWithHelpers\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original 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# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\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    if ! command -v java >/dev/null 2>&1\n    then\n        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.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\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# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "rootProject.name = \"alchemist\"\n\npluginManagement {\n    repositories {\n        mavenCentral()\n    }\n}\n\ndependencyResolutionManagement {\n    repositories {\n        mavenCentral()\n    }\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/internal/SaturatingLong.kt",
    "content": "@file:Suppress(\"NOTHING_TO_INLINE\")\n\npackage io.github.kevincianfarini.alchemist.internal\n\nimport kotlin.jvm.JvmInline\nimport kotlin.math.absoluteValue\nimport kotlin.math.roundToLong\nimport kotlin.math.sign\n\n/**\n * A [SaturatingLong] is a 64-bit signed integer that protects against overflow during multiplication, addition, and\n * subtraction. When one of those operations overflows, either [POSITIVE_INFINITY] or [NEGATIVE_INFINITY] is returned.\n * Performing operations with an infinite value will either return another infinite value if it's a valid operation,\n * or will error if it's an invalid operation.\n *\n * See [Saturation Arithmetic Intrinsics](https://llvm.org/docs/LangRef.html#saturation-arithmetic-intrinsics) from\n * LLVM for more details.\n */\n@JvmInline\ninternal value class SaturatingLong(val rawValue: Long) {\n\n    operator fun plus(other: SaturatingLong): SaturatingLong = when {\n        isInfinite() -> when {\n            this == other || other.isFinite() -> this\n            else -> {\n                throwIllegalArgumentException(\"Summing infinite values of different signs yields an undefined result.\")\n            }\n        }\n        else -> {\n            val a = rawValue\n            val b = other.rawValue\n            val result = rawValue + other.rawValue\n            val didOverflow = (((a and b and result.inv()) or (a.inv() and b.inv() and result)) < 0L)\n            when {\n                didOverflow -> if (result > 0) NEGATIVE_INFINITY else POSITIVE_INFINITY\n                else -> SaturatingLong(result)\n            }\n        }\n    }\n\n    inline operator fun plus(other: Long): SaturatingLong {\n        return this + SaturatingLong(other)\n    }\n\n    inline operator fun minus(other: SaturatingLong): SaturatingLong = this + (-other)\n\n    inline operator fun minus(other: Long): SaturatingLong {\n        return this - SaturatingLong(other)\n    }\n\n    operator fun unaryMinus(): SaturatingLong = when(this) {\n        POSITIVE_INFINITY -> NEGATIVE_INFINITY\n        NEGATIVE_INFINITY -> POSITIVE_INFINITY\n        else -> SaturatingLong(-rawValue)\n    }\n\n    operator fun times(other: SaturatingLong): SaturatingLong {\n        return when {\n            isInfinite() or other.isInfinite() -> when {\n                rawValue == 0L || other.rawValue == 0L -> {\n                    throwIllegalArgumentException(\"Multiplying an infinite value by zero yields an undefined result.\")\n                }\n                rawValue.sign == other.rawValue.sign -> POSITIVE_INFINITY\n                else -> NEGATIVE_INFINITY\n            }\n            else -> {\n                val result = rawValue * other.rawValue\n                val doesOverflow = rawValue != 0L && result / rawValue != other.rawValue\n                when {\n                    doesOverflow && rawValue.sign == other.rawValue.sign -> POSITIVE_INFINITY\n                    doesOverflow -> NEGATIVE_INFINITY\n                    else -> SaturatingLong(result)\n                }\n            }\n        }\n    }\n\n    val sign: Int inline get() = rawValue.sign\n\n    inline operator fun times(other: Long): SaturatingLong {\n        return this * SaturatingLong(other)\n    }\n\n    inline operator fun times(other: Int): SaturatingLong {\n        return times(other.toLong())\n    }\n\n    inline operator fun times(other: Double): SaturatingLong {\n        val longScale = other.roundToLong()\n        if (longScale.toDouble() == other) {\n            return times(longScale)\n        } else {\n            val thisDouble = toDouble()\n            val result = thisDouble * other\n            return SaturatingLong(result.roundToLong())\n        }\n    }\n\n    operator fun div(other: SaturatingLong): SaturatingLong {\n        val thisInfinite = isInfinite()\n        val otherInfinite = other.isInfinite()\n        return when {\n            thisInfinite && otherInfinite -> {\n                throwIllegalArgumentException(\"Dividing two infinite values yields an undefined result.\")\n            }\n            thisInfinite -> this * other.sign\n            otherInfinite -> SaturatingLong(0)\n            else -> SaturatingLong(rawValue / other.rawValue)\n        }\n    }\n\n    operator fun div(other: Long): SaturatingLong {\n        return this / SaturatingLong(other)\n    }\n\n    operator fun div(other: Int): SaturatingLong {\n        return div(other.toLong())\n    }\n\n    operator fun div(other: Double): SaturatingLong {\n        val longScale = other.roundToLong()\n        if (longScale.toDouble() == other) {\n            return div(longScale)\n        } else {\n            val thisDouble = toDouble()\n            val result = thisDouble / other\n            return SaturatingLong(result.roundToLong())\n        }\n    }\n\n    operator fun rem(other: SaturatingLong): SaturatingLong {\n        val thisInfinite = isInfinite()\n        val otherInfinite = other.isInfinite()\n        return when {\n            thisInfinite && otherInfinite -> {\n                throwIllegalArgumentException(\"Dividing two infinite values yields an undefined result.\")\n            }\n            thisInfinite -> this * other.sign\n            otherInfinite -> SaturatingLong(0)\n            else -> SaturatingLong(rawValue % other.rawValue)\n        }\n    }\n\n    operator fun rem(other: Long): SaturatingLong {\n        return this % SaturatingLong(other)\n    }\n\n    operator fun compareTo(other: SaturatingLong): Int {\n        return rawValue.compareTo(other.rawValue)\n    }\n\n    operator fun compareTo(other: Long): Int {\n        return compareTo(SaturatingLong(other))\n    }\n\n    operator fun compareTo(other: Int): Int {\n        return compareTo(SaturatingLong(other.toLong()))\n    }\n\n    override fun toString(): String = when (this) {\n        POSITIVE_INFINITY -> \"Infinity\"\n        NEGATIVE_INFINITY -> \"-Infinity\"\n        else -> rawValue.toString()\n    }\n\n    inline fun isInfinite(): Boolean {\n        return (this == POSITIVE_INFINITY) or (this == NEGATIVE_INFINITY)\n    }\n\n    inline fun isFinite(): Boolean = !isInfinite()\n\n    val absoluteValue: SaturatingLong\n        get() = when {\n            isInfinite() -> POSITIVE_INFINITY\n            else -> SaturatingLong(rawValue.absoluteValue)\n        }\n\n    fun toDouble(): Double = when (this) {\n        POSITIVE_INFINITY -> Double.POSITIVE_INFINITY\n        NEGATIVE_INFINITY -> Double.NEGATIVE_INFINITY\n        else -> rawValue.toDouble()\n    }\n}\n\ninternal inline val Long.saturated get() = SaturatingLong(this)\n\n// Inline the constants versus using `MIN_VALUE` and `MAX_VALUE` to avoid accessing Long's companion.\ninternal inline val POSITIVE_INFINITY get() = SaturatingLong(9223372036854775807L)\ninternal inline val NEGATIVE_INFINITY get() = SaturatingLong(-9223372036854775807L - 1L)\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/internal/double.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\n/**\n * Returns a decimal string that has been rounded to the specified number of [decimals].\n */\ninternal expect fun Double.toDecimalString(decimals: Int): String"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/internal/duration.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\nimport io.github.kevincianfarini.alchemist.type.Energy\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.microseconds\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.nanoseconds\nimport kotlin.time.Duration.Companion.seconds\nimport kotlin.time.DurationUnit\n\ninternal fun Duration.isPreciseToNanosecond(): Boolean {\n    return this == inWholeNanoseconds.nanoseconds\n}\n\ninternal val Duration.sign: Int get() = when {\n    isPositive() -> 1\n    isNegative() -> -1\n    else -> 0\n}\n\ninternal val DurationUnit.shortName: String get() = when (this) {\n    DurationUnit.NANOSECONDS -> \"ns\"\n    DurationUnit.MICROSECONDS -> \"us\"\n    DurationUnit.MILLISECONDS -> \"ms\"\n    DurationUnit.SECONDS -> \"s\"\n    DurationUnit.MINUTES -> \"m\"\n    DurationUnit.HOURS -> \"h\"\n    DurationUnit.DAYS -> \"d\"\n    else -> error(\"Unknown unit: $this\")\n}\n\ninternal val DurationUnit.shortNameSquared: String get() = when (this) {\n    DurationUnit.NANOSECONDS -> \"ns²\"\n    DurationUnit.MICROSECONDS -> \"us²\"\n    DurationUnit.MILLISECONDS -> \"ms²\"\n    DurationUnit.SECONDS -> \"s²\"\n    DurationUnit.MINUTES -> \"m²\"\n    DurationUnit.HOURS -> \"h²\"\n    DurationUnit.DAYS -> \"d²\"\n    else -> error(\"Unknown unit: $this\")\n}\n\ninternal val DurationUnit.secondScale: Double get() = when (this) {\n    DurationUnit.NANOSECONDS -> 0.000_000_001\n    DurationUnit.MICROSECONDS -> 0.000_001\n    DurationUnit.MILLISECONDS -> 0.001\n    DurationUnit.SECONDS -> 1.0\n    DurationUnit.MINUTES -> 60.0\n    DurationUnit.HOURS -> 3_600.0\n    DurationUnit.DAYS -> 86_400.0\n    else -> error(\"Unknown unit: $this\")\n}\n\ninternal fun Duration.toDecimalComponents(\n    action: (\n        kiloseconds: Long,\n        seconds: Long,\n        millis: Long,\n        micros: Long,\n        nanos: Long\n    ) -> Energy,\n): Energy {\n    val seconds = inWholeSeconds\n    val secondsRemainder = this - seconds.seconds\n    val millis = secondsRemainder.inWholeMilliseconds\n    val millisRemainder = secondsRemainder - millis.milliseconds\n    val micros = millisRemainder.inWholeMicroseconds\n    val nanos = (millisRemainder - micros.microseconds).inWholeNanoseconds\n    return action(seconds / 1_000, seconds % 1_000, millis, micros, nanos)\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/internal/exception.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\n/**\n * Ensures that we don't inline the exception throwing instructions and instead make the jump\n * to this function. This helps avoid busting the instruction cache and localizes exception throwing\n * instructions to one place.\n *\n * TODO: Supply a ProGuard rule to keep this method so it's not inlined\n */\ninternal fun throwIllegalArgumentException(message: String): Nothing {\n    throw IllegalArgumentException(message)\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/acceleration.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Acceleration\n\ninternal val Int.nmPerSecond2: Acceleration get() = toLong().nmPerSecond2\ninternal val Long.nmPerSecond2: Acceleration get() = Acceleration(saturated)\ninternal val SaturatingLong.nmPerSecond2: Acceleration get() = Acceleration(this)\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/area.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Area\nimport io.github.kevincianfarini.alchemist.unit.AreaUnit\nimport kotlin.math.roundToLong\n\n/**\n * Returns an [Area] equal to [Int] number of decimilliares.\n */\npublic inline val Int.decimilliares: Area get() = toArea(AreaUnit.Metric.Decimilliare)\n\n/**\n * Returns an [Area] equal to [Long] number of decimilliares.\n */\npublic inline val Long.decimilliares: Area get() = toArea(AreaUnit.Metric.Decimilliare)\n\n/**\n * Returns an [Area] equal to [Double] number of decimilliares. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.decimilliares: Area get() = toArea(AreaUnit.Metric.Decimilliare)\n\n/**\n * Returns an [Area] equal to [Int] number of centiares.\n */\npublic inline val Int.centiares: Area get() = toArea(AreaUnit.Metric.Centiare)\n\n/**\n * Returns an [Area] equal to [Long] number of centiares.\n */\npublic inline val Long.centiares: Area get() = toArea(AreaUnit.Metric.Centiare)\n\n/**\n * Returns an [Area] equal to [Double] number of centiares. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.centiares: Area get() = toArea(AreaUnit.Metric.Centiare)\n\n/**\n * Returns an [Area] equal to [Int] number of deciares.\n */\npublic inline val Int.deciares: Area get() = toArea(AreaUnit.Metric.Deciare)\n\n/**\n * Returns an [Area] equal to [Long] number of deciares.\n */\npublic inline val Long.deciares: Area get() = toArea(AreaUnit.Metric.Deciare)\n\n/**\n * Returns an [Area] equal to [Double] number of deciares. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.deciares: Area get() = toArea(AreaUnit.Metric.Deciare)\n\n/**\n * Returns an [Area] equal to [Int] number of ares.\n */\npublic inline val Int.ares: Area get() = toArea(AreaUnit.Metric.Are)\n\n/**\n * Returns an [Area] equal to [Long] number of ares.\n */\npublic inline val Long.ares: Area get() = toArea(AreaUnit.Metric.Are)\n\n/**\n * Returns an [Area] equal to [Double] number of ares. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.ares: Area get() = toArea(AreaUnit.Metric.Are)\n\n/**\n * Returns an [Area] equal to [Int] number of decares.\n */\npublic inline val Int.decares: Area get() = toArea(AreaUnit.Metric.Decare)\n\n/**\n * Returns an [Area] equal to [Long] number of decares.\n */\npublic inline val Long.decares: Area get() = toArea(AreaUnit.Metric.Decare)\n\n/**\n * Returns an [Area] equal to [Double] number of decares. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.decares: Area get() = toArea(AreaUnit.Metric.Decare)\n\n/**\n * Returns an [Area] equal to [Int] number of hectares.\n */\npublic inline val Int.hectares: Area get() = toArea(AreaUnit.Metric.Hectare)\n\n/**\n * Returns an [Area] equal to [Long] number of hectares.\n */\npublic inline val Long.hectares: Area get() = toArea(AreaUnit.Metric.Hectare)\n\n/**\n * Returns an [Area] equal to [Double] number of hectares. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.hectares: Area get() = toArea(AreaUnit.Metric.Hectare)\n\n/**\n * Returns an [Area] equal to [Int] number of the specified [unit].\n */\npublic fun Int.toArea(unit: AreaUnit): Area = toLong().toArea(unit)\n\n/**\n * Returns an [Area] equal to [Long] number of the specified [unit].\n */\npublic fun Long.toArea(unit: AreaUnit): Area = Area(saturated * unit.millimetersSquaredScale)\n\n/**\n * Returns an [Area] equal to [Double] number of the specified [unit]. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic fun Double.toArea(unit: AreaUnit): Area {\n    val valueInMillimeters2 = this * unit.millimetersSquaredScale\n    require(!valueInMillimeters2.isNaN()) { \"Area value cannot be NaN.\" }\n    return Area(valueInMillimeters2.roundToLong().saturated)\n}\n\ninternal inline val Long.mm2: Area get() = Area(saturated)\ninternal inline val Int.mm2: Area get() = Area(toLong().saturated)\ninternal inline val SaturatingLong.mm2: Area get() = Area(this)\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/energy.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Energy\nimport io.github.kevincianfarini.alchemist.unit.EnergyUnit\nimport kotlin.math.roundToLong\n\n/**\n * Returns an [Energy] equal to [Int] number of millijoules.\n */\npublic inline val Int.millijoules: Energy get() = toEnergy(EnergyUnit.International.Millijoule)\n\n/**\n * Returns an [Energy] equal to [Long] number of millijoules.\n */\npublic inline val Long.millijoules: Energy get() = toEnergy(EnergyUnit.International.Millijoule)\ninternal inline val SaturatingLong.millijoules get() = Energy(this)\n\n/**\n * Returns an [Energy] equal to [Int] number of joules.\n */\npublic inline val Int.joules: Energy get() = toEnergy(EnergyUnit.International.Joule)\n\n/**\n * Returns an [Energy] equal to [Long] number of joules.\n */\npublic inline val Long.joules: Energy get() = toEnergy(EnergyUnit.International.Joule)\n\n/**\n * Returns an [Energy] equal to [Double] number of joules. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.joules: Energy get() = toEnergy(EnergyUnit.International.Joule)\n\n/**\n * Returns an [Energy] equal to [Int] number of kilojoules.\n */\npublic inline val Int.kilojoules: Energy get() = toEnergy(EnergyUnit.International.Kilojoule)\n\n/**\n * Returns an [Energy] equal to [Long] number of kilojoules.\n */\npublic inline val Long.kilojoules: Energy get() = toEnergy(EnergyUnit.International.Kilojoule)\n\n/**\n * Returns an [Energy] equal to [Double] number of kilojoules. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.kilojoules: Energy get() = toEnergy(EnergyUnit.International.Kilojoule)\n\n/**\n * Returns an [Energy] equal to [Int] number of megajoules.\n */\npublic inline val Int.megajoules: Energy get() = toEnergy(EnergyUnit.International.Megajoule)\n\n/**\n * Returns an [Energy] equal to [Long] number of megajoules.\n */\npublic inline val Long.megajoules: Energy get() = toEnergy(EnergyUnit.International.Megajoule)\n\n/**\n * Returns an [Energy] equal to [Double] number of megajoules. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.megajoules: Energy get() = toEnergy(EnergyUnit.International.Megajoule)\n\n/**\n * Returns an [Energy] equal to [Int] number of gigajoules.\n */\npublic inline val Int.gigajoules: Energy get() = toEnergy(EnergyUnit.International.Gigajoule)\n\n/**\n * Returns an [Energy] equal to [Long] number of gigajoules.\n */\npublic inline val Long.gigajoules: Energy get() = toEnergy(EnergyUnit.International.Gigajoule)\n\n/**\n * Returns an [Energy] equal to [Double] number of gigajoules. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.gigajoules: Energy get() = toEnergy(EnergyUnit.International.Gigajoule)\n\n/**\n * Returns an [Energy] equal to [Int] number of tetrajoules.\n */\npublic inline val Int.tetrajoules: Energy get() = toEnergy(EnergyUnit.International.Tetrajoule)\n\n/**\n * Returns an [Energy] equal to [Long] number of tetrajoules.\n */\npublic inline val Long.tetrajoules: Energy get() = toEnergy(EnergyUnit.International.Tetrajoule)\n\n/**\n * Returns an [Energy] equal to [Double] number of tetrajoules. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.tetrajoules: Energy get() = toEnergy(EnergyUnit.International.Tetrajoule)\n\n/**\n * Returns an [Energy] equal to [Int] number of petajoules.\n */\npublic inline val Int.petajoules: Energy get() = toEnergy(EnergyUnit.International.Petajoule)\n\n/**\n * Returns an [Energy] equal to [Long] number of petajoules.\n */\npublic inline val Long.petajoules: Energy get() = toEnergy(EnergyUnit.International.Petajoule)\n\n/**\n * Returns an [Energy] equal to [Double] number of petajoules. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.petajoules: Energy get() = toEnergy(EnergyUnit.International.Petajoule)\n\n/**\n * Returns an [Energy] equal to [Int] number of milliwatt-hours.\n */\npublic inline val Int.milliwattHours: Energy get() = toEnergy(EnergyUnit.Electricity.MilliwattHour)\n\n/**\n * Returns an [Energy] equal to [Long] number of milliwatt-hours.\n */\npublic inline val Long.milliwattHours: Energy get() = toEnergy(EnergyUnit.Electricity.MilliwattHour)\n\n/**\n * Returns an [Energy] equal to [Double] number of milliwatt-hours. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.milliwattHours: Energy get() = toEnergy(EnergyUnit.Electricity.MilliwattHour)\n\n/**\n * Returns an [Energy] equal to [Int] number of watt-hours.\n */\npublic inline val Int.wattHours: Energy get() = toEnergy(EnergyUnit.Electricity.WattHour)\n\n/**\n * Returns an [Energy] equal to [Long] number of watt-hours.\n */\npublic inline val Long.wattHours: Energy get() = toEnergy(EnergyUnit.Electricity.WattHour)\n\n/**\n * Returns an [Energy] equal to [Double] number of watt-hours. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.wattHours: Energy get() = toEnergy(EnergyUnit.Electricity.WattHour)\n\n/**\n * Returns an [Energy] equal to [Int] number of kilowatt-hours.\n */\npublic inline val Int.kilowattHours: Energy get() = toEnergy(EnergyUnit.Electricity.KilowattHour)\n\n/**\n * Returns an [Energy] equal to [Long] number of kilowatt-hours.\n */\npublic inline val Long.kilowattHours: Energy get() = toEnergy(EnergyUnit.Electricity.KilowattHour)\n\n/**\n * Returns an [Energy] equal to [Double] number of kilowatt-hours. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.kilowattHours: Energy get() = toEnergy(EnergyUnit.Electricity.KilowattHour)\n\n/**\n * Returns an [Energy] equal to [Int] number of megawatt-hours.\n */\npublic inline val Int.megawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.MegawattHour)\n\n/**\n * Returns an [Energy] equal to [Long] number of megawatt-hours.\n */\npublic inline val Long.megawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.MegawattHour)\n\n/**\n * Returns an [Energy] equal to [Double] number of megawatt-hours. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.megawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.MegawattHour)\n\n/**\n * Returns an [Energy] equal to [Int] number of gigawatt-hours.\n */\npublic inline val Int.gigawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.GigawattHour)\n\n/**\n * Returns an [Energy] equal to [Long] number of gigawatt-hours.\n */\npublic inline val Long.gigawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.GigawattHour)\n\n/**\n * Returns an [Energy] equal to [Double] number of gigawatt-hours. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.gigawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.GigawattHour)\n\n/**\n * Returns an [Energy] equal to [Int] number of terawatt-hours.\n */\npublic inline val Int.terawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.TerawattHour)\n\n/**\n * Returns an [Energy] equal to [Int] number of terawatt-hours.\n */\npublic inline val Long.terawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.TerawattHour)\n\n/**\n * Returns an [Energy] equal to [Double] number of terawatt-hours. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.terawattHours: Energy get() = toEnergy(EnergyUnit.Electricity.TerawattHour)\n\n/**\n * Returns an [Energy] equal to [Int] number of the specified [unit].\n */\npublic fun Int.toEnergy(unit: EnergyUnit): Energy {\n    return toLong().toEnergy(unit)\n}\n\n/**\n * Returns an [Energy] equal to [Long] number of the specified [unit].\n */\npublic fun Long.toEnergy(unit: EnergyUnit): Energy {\n    return Energy(this.saturated * unit.millijouleScale)\n}\n\n/**\n * Returns an [Energy] equal to [Double] number of the specified [unit]. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic fun Double.toEnergy(unit: EnergyUnit): Energy {\n    val valueInMillijoules = this * unit.millijouleScale\n    require(!valueInMillijoules.isNaN()) { \"Energy value cannot be NaN.\" }\n    return Energy(valueInMillijoules.roundToLong().saturated)\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/force.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Force\nimport io.github.kevincianfarini.alchemist.unit.ForceUnit\nimport kotlin.math.roundToLong\n\n/**\n * Returns an [Force] equal to [Int] number of nanonewtons.\n */\npublic inline val Int.nanonewtons: Force get() = toForce(ForceUnit.International.Nanonewton)\n\n/**\n * Returns an [Force] equal to [Long] number of nanonewtons.\n */\npublic inline val Long.nanonewtons: Force get() = toForce(ForceUnit.International.Nanonewton)\n\n/**\n * Returns an [Force] equal to [Int] number of micronewtons.\n */\npublic inline val Int.micronewtons: Force get() = toForce(ForceUnit.International.Micronewton)\n\n/**\n * Returns an [Force] equal to [Long] number of micronewtons.\n */\npublic inline val Long.micronewtons: Force get() = toForce(ForceUnit.International.Micronewton)\n\n/**\n * Returns an [Force] equal to [Double] number of micronewtons. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.micronewtons: Force get() = toForce(ForceUnit.International.Micronewton)\n\n/**\n * Returns an [Force] equal to [Int] number of millinewtons.\n */\npublic inline val Int.millinewtons: Force get() = toForce(ForceUnit.International.Millinewton)\n\n/**\n * Returns an [Force] equal to [Long] number of millinewtons.\n */\npublic inline val Long.millinewtons: Force get() = toForce(ForceUnit.International.Millinewton)\n\n/**\n * Returns an [Force] equal to [Double] number of millinewtons. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.millinewtons: Force get() = toForce(ForceUnit.International.Millinewton)\n\n/**\n * Returns an [Force] equal to [Int] number of newtons.\n */\npublic inline val Int.newtons: Force get() = toForce(ForceUnit.International.Newton)\n\n/**\n * Returns an [Force] equal to [Long] number of newtons.\n */\npublic inline val Long.newtons: Force get() = toForce(ForceUnit.International.Newton)\n\n/**\n * Returns an [Force] equal to [Double] number of newtons. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.newtons: Force get() = toForce(ForceUnit.International.Newton)\n\n/**\n * Returns an [Force] equal to [Int] number of kilonewtons.\n */\npublic inline val Int.kilonewtons: Force get() = toForce(ForceUnit.International.Kilonewton)\n\n/**\n * Returns an [Force] equal to [Long] number of kilonewtons.\n */\npublic inline val Long.kilonewtons: Force get() = toForce(ForceUnit.International.Kilonewton)\n\n/**\n * Returns an [Force] equal to [Double] number of kilonewtons. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.kilonewtons: Force get() = toForce(ForceUnit.International.Kilonewton)\n\n/**\n * Returns an [Force] equal to [Int] number of meganewtons.\n */\npublic inline val Int.meganewtons: Force get() = toForce(ForceUnit.International.Meganewton)\n\n/**\n * Returns an [Force] equal to [Long] number of meganewtons.\n */\npublic inline val Long.meganewtons: Force get() = toForce(ForceUnit.International.Meganewton)\n\n/**\n * Returns an [Force] equal to [Double] number of meganewtons. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.meganewtons: Force get() = toForce(ForceUnit.International.Meganewton)\n\n/**\n * Returns an [Force] equal to [Int] number of giganewtons.\n */\npublic inline val Int.giganewtons: Force get() = toForce(ForceUnit.International.Giganewton)\n\n/**\n * Returns an [Force] equal to [Long] number of giganewtons.\n */\npublic inline val Long.giganewtons: Force get() = toForce(ForceUnit.International.Giganewton)\n\n/**\n * Returns an [Force] equal to [Double] number of giganewtons. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.giganewtons: Force get() = toForce(ForceUnit.International.Giganewton)\n\n/**\n * Returns an [Force] equal to [Int] number of the specified [unit].\n */\npublic fun Int.toForce(unit: ForceUnit): Force = toLong().toForce(unit)\n\n/**\n * Returns an [Force] equal to [Long] number of the specified [unit].\n */\npublic fun Long.toForce(unit: ForceUnit): Force = Force(saturated * unit.nanonewtonScale)\n\n/**\n * Returns an [Force] equal to [Double] number of the specified [unit]. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic fun Double.toForce(unit: ForceUnit): Force {\n    val valueInNanonewtons = this * unit.nanonewtonScale\n    require(!valueInNanonewtons.isNaN()) { \"Force value cannot be NaN.\" }\n    return Force(valueInNanonewtons.roundToLong().saturated)\n}\n\ninternal inline val SaturatingLong.kilonewtons: Force get() = rawValue.toForce(ForceUnit.International.Kilonewton)"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/length.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Length\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport kotlin.math.roundToLong\n\n\n/**\n * Returns a [Length] equal to [Int] number of nanometers.\n */\npublic inline val Int.nanometers: Length get() = toLength(LengthUnit.International.Nanometer)\n\n/**\n * Returns a [Length] equal to [Long] number of nanometers.\n */\npublic inline val Long.nanometers: Length get() = toLength(LengthUnit.International.Nanometer)\n\ninternal inline val SaturatingLong.nanometers: Length get() = Length(this)\n\n/**\n * Returns a [Length] equal to [Int] number of micrometers.\n */\npublic inline val Int.micrometers: Length get() = toLength(LengthUnit.International.Micrometer)\n\n/**\n * Returns a [Length] equal to [Long] number of micrometers.\n */\npublic inline val Long.micrometers: Length get() = toLength(LengthUnit.International.Micrometer)\n\n/**\n * Returns a [Length] equal to [Double] number of micrometers. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.micrometers: Length get() = toLength(LengthUnit.International.Micrometer)\n\n/**\n * Returns a [Length] equal to [Int] number of millimeters.\n */\npublic inline val Int.millimeters: Length get() = toLength(LengthUnit.International.Millimeter)\n\n/**\n * Returns a [Length] equal to [Long] number of millimeters.\n */\npublic inline val Long.millimeters: Length get() = toLength(LengthUnit.International.Millimeter)\n\n/**\n * Returns a [Length] equal to [Double] number of millimeters. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.millimeters: Length get() = toLength(LengthUnit.International.Millimeter)\n\n/**\n * Returns a [Length] equal to [Int] number of centimeters.\n */\npublic inline val Int.centimeters: Length get() = toLength(LengthUnit.International.Centimeter)\n\n/**\n * Returns a [Length] equal to [Long] number of centimeters.\n */\npublic inline val Long.centimeters: Length get() = toLength(LengthUnit.International.Centimeter)\n\ninternal inline val SaturatingLong.centimeters: Length get() = rawValue.toLength(LengthUnit.International.Centimeter)\n\n/**\n * Returns a [Length] equal to [Double] number of centimeters. Depending on its magnitude, some precision may be lost.\n */\npublic inline val Double.centimeters: Length get() = toLength(LengthUnit.International.Centimeter)\n\n/**\n * Returns a [Length] equal to [Int] number of meters.\n */\npublic inline val Int.meters: Length get() = toLength(LengthUnit.International.Meter)\n\n/**\n * Returns a [Length] equal to [Long] number of meters.\n */\npublic inline val Long.meters: Length get() = toLength(LengthUnit.International.Meter)\n\n/**\n * Returns a [Length] equal to [Double] number of meters. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.meters: Length get() = toLength(LengthUnit.International.Meter)\n\n/**\n * Returns a [Length] equal to [Int] number of kilometers.\n */\npublic inline val Int.kilometers: Length get() = toLength(LengthUnit.International.Kilometer)\n\n/**\n * Returns a [Length] equal to [Long] number of kilometers.\n */\npublic inline val Long.kilometers: Length get() = toLength(LengthUnit.International.Kilometer)\n\n/**\n * Returns a [Length] equal to [Double] number of kilometers. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.kilometers: Length get() = toLength(LengthUnit.International.Kilometer)\n\n/**\n * Returns a [Length] equal to [Int] number of megameters.\n */\npublic inline val Int.megameters: Length get() = toLength(LengthUnit.International.Megameter)\n\n/**\n * Returns a [Length] equal to [Long] number of megameters.\n */\npublic inline val Long.megameters: Length get() = toLength(LengthUnit.International.Megameter)\n\n/**\n * Returns a [Length] equal to [Double] number of megameters. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.megameters: Length get() = toLength(LengthUnit.International.Megameter)\n\n/**\n * Returns a [Length] equal to [Int] number of gigameters.\n */\npublic inline val Int.gigameters: Length get() = toLength(LengthUnit.International.Gigameter)\n\n/**\n * Returns a [Length] equal to [Long] number of gigameters.\n */\npublic inline val Long.gigameters: Length get() = toLength(LengthUnit.International.Gigameter)\n\n/**\n * Returns a [Length] equal to [Double] number of gigameters. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.gigameters: Length get() = toLength(LengthUnit.International.Gigameter)\n\n/**\n * Returns a [Length] equal to [Int] number of inches.\n */\npublic inline val Int.inches: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Inch)\n\n/**\n * Returns a [Length] equal to [Long] number of inches.\n */\npublic inline val Long.inches: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Inch)\n\n/**\n * Returns a [Length] equal to [Double] number of inches. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.inches: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Inch)\n\n/**\n * Returns a [Length] equal to [Int] number of feet.\n */\npublic inline val Int.feet: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Foot)\n\n/**\n * Returns a [Length] equal to [Long] number of feet.\n */\npublic inline val Long.feet: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Foot)\n\n/**\n * Returns a [Length] equal to [Double] number of feet. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.feet: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Foot)\n\n/**\n * Returns a [Length] equal to [Int] number of yards.\n */\npublic inline val Int.yards: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Yard)\n\n/**\n * Returns a [Length] equal to [Long] number of yards.\n */\npublic inline val Long.yards: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Yard)\n\n/**\n * Returns a [Length] equal to [Double] number of yards. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.yards: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Yard)\n\n/**\n * Returns a [Length] equal to [Int] number of miles.\n */\npublic inline val Int.miles: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Mile)\n\n/**\n * Returns a [Length] equal to [Long] number of miles.\n */\npublic inline val Long.miles: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Mile)\n\n/**\n * Returns a [Length] equal to [Double] number of miles. Depending on its magnitude, some precision may be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.miles: Length get() = toLength(LengthUnit.UnitedStatesCustomary.Mile)\n\n/**\n * Returns a [Length] equal to [Int] number of the specified [unit].\n */\npublic fun Int.toLength(unit: LengthUnit): Length {\n    return toLong().toLength(unit)\n}\n\n/**\n * Returns a [Length] equal to [Long] number of the specified [unit].\n */\npublic fun Long.toLength(unit: LengthUnit): Length {\n    return Length(this.saturated * unit.nanometerScale)\n}\n\n/**\n * Returns a [Length] equal to [Double] number of the specified [unit]. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic fun Double.toLength(unit: LengthUnit): Length {\n    val valueInNanometers = this * unit.nanometerScale\n    require(!valueInNanometers.isNaN()) { \"Length value cannot be NaN.\" }\n    return Length(valueInNanometers.roundToLong().saturated)\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/mass.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Mass\nimport io.github.kevincianfarini.alchemist.unit.MassUnit\nimport kotlin.math.roundToLong\n\n/**\n * Returns a [Mass] equal to [Int] number of micrograms.\n */\npublic inline val Int.micrograms: Mass get() = toMass(MassUnit.International.Microgram)\n\n/**\n * Returns a [Mass] equal to [Long] number of micrograms.\n */\npublic inline val Long.micrograms: Mass get() = toMass(MassUnit.International.Microgram)\n\n/**\n * Returns a [Mass] equal to [Int] number of milligrams.\n */\npublic inline val Int.milligrams: Mass get() = toMass(MassUnit.International.Milligram)\n\n/**\n * Returns a [Mass] equal to [Long] number of milligrams.\n */\npublic inline val Long.milligrams: Mass get() = toMass(MassUnit.International.Milligram)\n\n/**\n * Returns a [Mass] equal to [Double] number of milligrams. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.milligrams: Mass get() = toMass(MassUnit.International.Milligram)\n\n/**\n * Returns a [Mass] equal to [Int] number of grams.\n */\npublic inline val Int.grams: Mass get() = toMass(MassUnit.International.Gram)\n\n/**\n * Returns a [Mass] equal to [Long] number of grams.\n */\npublic inline val Long.grams: Mass get() = toMass(MassUnit.International.Gram)\n\n/**\n * Returns a [Mass] equal to [Double] number of grams. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.grams: Mass get() = toMass(MassUnit.International.Gram)\n\n/**\n * Returns a [Mass] equal to [Int] number of kilograms.\n */\npublic inline val Int.kilograms: Mass get() = toMass(MassUnit.International.Kilogram)\n\n/**\n * Returns a [Mass] equal to [Long] number of kilograms.\n */\npublic inline val Long.kilograms: Mass get() = toMass(MassUnit.International.Kilogram)\n\n/**\n * Returns a [Mass] equal to [Double] number of kilograms. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.kilograms: Mass get() = toMass(MassUnit.International.Kilogram)\n\n/**\n * Returns a [Mass] equal to [Int] number of megagrams.\n */\npublic inline val Int.megagrams: Mass get() = toMass(MassUnit.International.Megagram)\n\n/**\n * Returns a [Mass] equal to [Long] number of megagrams.\n */\npublic inline val Long.megagrams: Mass get() = toMass(MassUnit.International.Megagram)\n\n/**\n * Returns a [Mass] equal to [Double] number of megagrams. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.megagrams: Mass get() = toMass(MassUnit.International.Megagram)\n\n/**\n * Returns a [Mass] equal to [Int] number of metric tonnes.\n */\npublic inline val Int.metricTonnes: Mass get() = toMass(MassUnit.International.Megagram)\n\n/**\n * Returns a [Mass] equal to [Long] number of metric tonnes.\n */\npublic inline val Long.metricTonnes: Mass get() = toMass(MassUnit.International.Megagram)\n\n/**\n * Returns a [Mass] equal to [Double] number of metric tonnes. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.metricTonnes: Mass get() = toMass(MassUnit.International.Megagram)\n\n/**\n * Returns a [Mass] equal to [Int] number of gigagrams.\n */\npublic inline val Int.gigagrams: Mass get() = toMass(MassUnit.International.Gigagram)\n\n/**\n * Returns a [Mass] equal to [Long] number of gigagrams.\n */\npublic inline val Long.gigagrams: Mass get() = toMass(MassUnit.International.Gigagram)\n\n/**\n * Returns a [Mass] equal to [Double] number of gigagrams. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.gigagrams: Mass get() = toMass(MassUnit.International.Gigagram)\n\n/**\n * Returns a [Mass] equal to [Int] number of teragrams.\n */\npublic inline val Int.teragrams: Mass get() = toMass(MassUnit.International.Teragram)\n\n/**\n * Returns a [Mass] equal to [Long] number of teragrams.\n */\npublic inline val Long.teragrams: Mass get() = toMass(MassUnit.International.Teragram)\n\n/**\n * Returns a [Mass] equal to [Double] number of teragrams. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.teragrams: Mass get() = toMass(MassUnit.International.Teragram)\n\n/**\n * Returns a [Mass] equal to [Int] number of the specified [unit].\n */\npublic fun Int.toMass(unit: MassUnit): Mass {\n    return toLong().toMass(unit)\n}\n\n/**\n * Returns a [Mass] equal to [Long] number of the specified [unit].\n */\npublic fun Long.toMass(unit: MassUnit): Mass {\n    return Mass(saturated * unit.microgramScale)\n}\n\n/**\n * Returns a [Mass] equal to [Double] number of the specified [unit]. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic fun Double.toMass(unit: MassUnit): Mass {\n    val valueInMicrograms = this * unit.microgramScale\n    require(!valueInMicrograms.isNaN()) { \"Mass value cannot be NaN.\" }\n    return Mass(valueInMicrograms.roundToLong().saturated)\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/power.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Power\nimport io.github.kevincianfarini.alchemist.unit.PowerUnit\nimport kotlin.math.roundToLong\n\n/**\n * Returns a [Power] equal to [Int] number of terawatts.\n */\npublic inline val Int.terawatts: Power get() = toPower(PowerUnit.International.Terawatt)\n\n/**\n * Returns a [Power] equal to [Long] number of terawatts.\n */\npublic inline val Long.terawatts: Power get() = toPower(PowerUnit.International.Terawatt)\n\n/**\n * Returns a [Power] equal to [Double] number of terawatts. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.terawatts: Power get() = toPower(PowerUnit.International.Terawatt)\n\n/**\n * Returns a [Power] equal to [Int] number of gigawatts.\n */\npublic inline val Int.gigawatts: Power get() = toPower(PowerUnit.International.Gigawatt)\n\n/**\n * Returns a [Power] equal to [Long] number of gigawatts.\n */\npublic inline val Long.gigawatts: Power get() = toPower(PowerUnit.International.Gigawatt)\n\n/**\n * Returns a [Power] equal to [Double] number of gigawatts. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.gigawatts: Power get() = toPower(PowerUnit.International.Gigawatt)\n\n/**\n * Returns a [Power] equal to [Int] number of megawatts.\n */\npublic inline val Int.megawatts: Power get() = toPower(PowerUnit.International.Megawatt)\n\n/**\n * Returns a [Power] equal to [Long] number of megawatts.\n */\npublic inline val Long.megawatts: Power get() = toPower(PowerUnit.International.Megawatt)\n\n/**\n * Returns a [Power] equal to [Double] number of megawatts. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.megawatts: Power get() = toPower(PowerUnit.International.Megawatt)\n\n/**\n * Returns a [Power] equal to [Int] number of kilowatts.\n */\npublic inline val Int.kilowatts: Power get() = toPower(PowerUnit.International.Kilowatt)\n\n/**\n * Returns a [Power] equal to [Long] number of kilowatts.\n */\npublic inline val Long.kilowatts: Power get() = toPower(PowerUnit.International.Kilowatt)\n\n/**\n * Returns a [Power] equal to [Double] number of kilowatts. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.kilowatts: Power get() = toPower(PowerUnit.International.Kilowatt)\n\n/**\n * Returns a [Power] equal to [Int] number of watts.\n */\npublic inline val Int.watts: Power get() = toPower(PowerUnit.International.Watt)\n\n/**\n * Returns a [Power] equal to [Long] number of watts.\n */\npublic inline val Long.watts: Power get() = toPower(PowerUnit.International.Watt)\n\n/**\n * Returns a [Power] equal to [Double] number of watts. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.watts: Power get() = toPower(PowerUnit.International.Watt)\n\n/**\n * Returns a [Power] equal to [Int] number of milliwatts.\n */\npublic inline val Int.milliwatts: Power get() = toPower(PowerUnit.International.Milliwatt)\n\n/**\n * Returns a [Power] equal to [Long] number of milliwatts.\n */\npublic inline val Long.milliwatts: Power get() = toPower(PowerUnit.International.Milliwatt)\n\n/**\n * Returns a [Power] equal to [Double] number of milliwatts. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.milliwatts: Power get() = toPower(PowerUnit.International.Milliwatt)\n\n/**\n * Returns a [Power] equal to [Int] number of microwatts.\n */\npublic inline val Int.microwatts: Power get() = toPower(PowerUnit.International.Microwatt)\n\n/**\n * Returns a [Power] equal to [Long] number of microwatts.\n */\npublic inline val Long.microwatts: Power get() = toPower(PowerUnit.International.Microwatt)\n\ninternal inline val SaturatingLong.megawatts get() = rawValue.toPower(PowerUnit.International.Megawatt)\ninternal inline val SaturatingLong.kilowatts get() = rawValue.toPower(PowerUnit.International.Kilowatt)\ninternal inline val SaturatingLong.watts get() = rawValue.toPower(PowerUnit.International.Watt)\ninternal inline val SaturatingLong.milliwatts get() = rawValue.toPower(PowerUnit.International.Milliwatt)\ninternal inline val SaturatingLong.microwatts get() = rawValue.toPower(PowerUnit.International.Microwatt)\n\n/**\n * Returns a [Power] equal to [Int] number of the specified [unit].\n */\npublic fun Int.toPower(unit: PowerUnit): Power {\n    return toLong().toPower(unit)\n}\n\n/**\n * Returns a [Power] equal to [Long] number of the specified [unit].\n */\npublic fun Long.toPower(unit: PowerUnit): Power {\n    return Power(saturated * unit.microwattScale)\n}\n\n/**\n * Returns a [Power] equal to [Double] number of the specified [unit]. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic fun Double.toPower(unit: PowerUnit): Power {\n    val valueInMicrowatts = this * unit.microwattScale\n    require(!valueInMicrowatts.isNaN()) { \"Mass value cannot be NaN.\" }\n    return Power(valueInMicrowatts.roundToLong().saturated)\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/temperature.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Temperature\nimport io.github.kevincianfarini.alchemist.unit.TemperatureUnit\nimport io.github.kevincianfarini.alchemist.unit.convertToNanokelvin\n\n/**\n * Returns a [Temperature] equal to [Int] number of nanokelvins.\n */\npublic val Int.nanokelvins: Temperature get() = toLong().nanokelvins\n\n/**\n * Returns a [Temperature] equal to [Long] number of nanokelvins.\n */\npublic val Long.nanokelvins: Temperature get() = saturated.nanokelvins\n\n/**\n * Returns a [Temperature] equal to [Int] number of microkelvins.\n */\npublic val Int.microkelvins: Temperature get() = toLong().microkelvins\n\n/**\n * Returns a [Temperature] equal to [Long] number of microkelvins.\n */\npublic val Long.microkelvins: Temperature get() = saturated.microkelvins\n\n/**\n * Returns a [Temperature] equal to [Double] number of microkelvins. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic val Double.microkelvins: Temperature get() = Temperature(\n    TemperatureUnit.International.Microkelvin.convertToNanokelvin(this).saturated\n)\n\n/**\n * Returns a [Temperature] equal to [Int] number of millikelvins.\n */\npublic val Int.millikelvins: Temperature get() = toLong().millikelvins\n\n/**\n * Returns a [Temperature] equal to [Long] number of millikelvins.\n */\npublic val Long.millikelvins: Temperature get() = saturated.millikelvins\n\n/**\n * Returns a [Temperature] equal to [Double] number of millikelvins. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic val Double.millikelvins: Temperature get() = Temperature(\n    TemperatureUnit.International.Millikelvin.convertToNanokelvin(this).saturated\n)\n\n/**\n * Returns a [Temperature] equal to [Int] number of kelvins.\n */\npublic val Int.kelvins: Temperature get() = toLong().kelvins\n\n/**\n * Returns a [Temperature] equal to [Long] number of kelvins.\n */\npublic val Long.kelvins: Temperature get() = saturated.kelvins\n\n/**\n * Returns a [Temperature] equal to [Double] number of kelvins. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic val Double.kelvins: Temperature get() = Temperature(\n    TemperatureUnit.International.Kelvin.convertToNanokelvin(this).saturated\n)\n\n/**\n * Returns a [Temperature] equal to [Int] number of kilokelvins.\n */\npublic val Int.kilokelvins: Temperature get() = toLong().kilokelvins\n\n/**\n * Returns a [Temperature] equal to [Long] number of kilokelvins.\n */\npublic val Long.kilokelvins: Temperature get() = saturated.kilokelvins\n\n/**\n * Returns a [Temperature] equal to [Double] number of kilokelvins. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic val Double.kilokelvins: Temperature get() = Temperature(\n    TemperatureUnit.International.Kilokelvin.convertToNanokelvin(this).saturated\n)\n\n/**\n * Returns a [Temperature] equal to [Int] number of megakelvins.\n */\npublic val Int.megakelvins: Temperature get() = toLong().megakelvins\n\n/**\n * Returns a [Temperature] equal to [Long] number of megakelvins.\n */\npublic val Long.megakelvins: Temperature get() = saturated.megakelvins\n\n/**\n * Returns a [Temperature] equal to [Double] number of megakelvins. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic val Double.megakelvins: Temperature get() = Temperature(\n    TemperatureUnit.International.Megakelvin.convertToNanokelvin(this).saturated\n)\n\n/**\n * Returns a [Temperature] equal to [Int] number of gigakelvins.\n */\npublic val Int.gigakelvins: Temperature get() = toLong().gigakelvins\n\n/**\n * Returns a [Temperature] equal to [Long] number of gigakelvins.\n */\npublic val Long.gigakelvins: Temperature get() = saturated.gigakelvins\n\n/**\n * Returns a [Temperature] equal to [Double] number of gigakelvins. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic val Double.gigakelvins: Temperature get() = Temperature(\n    TemperatureUnit.International.Gigakelvin.convertToNanokelvin(this).saturated\n)\n\n/**\n * Returns a [Temperature] equal to [Int] number of Celsius.\n */\npublic val Int.celsius: Temperature get() = toLong().celsius\n\n/**\n * Returns a [Temperature] equal to [Long] number of Celsius.\n */\npublic val Long.celsius: Temperature get() = saturated.celsius\n\n/**\n * Returns a [Temperature] equal to [Double] number of Celsius. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic val Double.celsius: Temperature get() = Temperature(\n    TemperatureUnit.International.Celsius.convertToNanokelvin(this).saturated\n)\n\n/**\n * Returns a [Temperature] equal to [Int] number of Fahrenheit.\n */\npublic val Int.fahrenheit: Temperature get() = toLong().fahrenheit\n\n/**\n * Returns a [Temperature] equal to [Long] number of Fahrenheit.\n */\npublic val Long.fahrenheit: Temperature get() = saturated.fahrenheit\n\n/**\n * Returns a [Temperature] equal to [Double] number of Fahrenheit. Depending on its magnitude, some precision may be\n * lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic val Double.fahrenheit: Temperature get() = Temperature(\n    TemperatureUnit.Fahrenheit.convertToNanokelvin(this).saturated\n)\n\nprivate inline val SaturatingLong.microkelvins get() = Temperature(\n    TemperatureUnit.International.Microkelvin.convertToNanokelvin(this)\n)\nprivate inline val SaturatingLong.nanokelvins get() = Temperature(\n    TemperatureUnit.International.Nanokelvin.convertToNanokelvin(this)\n)\nprivate inline val SaturatingLong.millikelvins get() = Temperature(\n    TemperatureUnit.International.Millikelvin.convertToNanokelvin(this)\n)\nprivate inline val SaturatingLong.kelvins get() = Temperature(\n    TemperatureUnit.International.Kelvin.convertToNanokelvin(this)\n)\nprivate inline val SaturatingLong.kilokelvins get() = Temperature(\n    TemperatureUnit.International.Kilokelvin.convertToNanokelvin(this)\n)\nprivate inline val SaturatingLong.megakelvins get() = Temperature(\n    TemperatureUnit.International.Megakelvin.convertToNanokelvin(this)\n)\nprivate inline val SaturatingLong.gigakelvins get() = Temperature(\n    TemperatureUnit.International.Gigakelvin.convertToNanokelvin(this)\n)\nprivate inline val SaturatingLong.celsius get() = Temperature(\n    TemperatureUnit.International.Celsius.convertToNanokelvin(this)\n)\nprivate inline val SaturatingLong.fahrenheit get() = Temperature(\n    TemperatureUnit.Fahrenheit.convertToNanokelvin(this)\n)\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/velocity.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Velocity\n\ninternal val Int.nmPerSecond: Velocity get() = Velocity(toLong().saturated)\ninternal val Long.nmPerSecond: Velocity get() = Velocity(saturated)\ninternal val SaturatingLong.nmPerSecond: Velocity get() = Velocity(this)\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/scalar/volume.kt",
    "content": "package io.github.kevincianfarini.alchemist.scalar\n\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.type.Volume\nimport io.github.kevincianfarini.alchemist.unit.VolumeUnit\nimport kotlin.math.roundToLong\n\n/**\n * Returns a [Volume] equal to [Int] number of milliliters.\n */\npublic inline val Int.milliliters: Volume get() = toVolume(VolumeUnit.Metric.Milliliter)\n\n/**\n * Returns a [Volume] equal to [Long] number of milliliters.\n */\npublic inline val Long.milliliters: Volume get() = toVolume(VolumeUnit.Metric.Milliliter)\n\n/**\n * Returns a [Volume] equal to [Int] number of liters.\n */\npublic inline val Int.liters: Volume get() = toVolume(VolumeUnit.Metric.Liter)\n\n/**\n * Returns a [Volume] equal to [Long] number of liters.\n */\npublic inline val Long.liters: Volume get() = toVolume(VolumeUnit.Metric.Liter)\n\n/**\n * Returns a [Volume] equal to [Double] number of liters. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.liters: Volume get() = toVolume(VolumeUnit.Metric.Liter)\n\n/**\n * Returns a [Volume] equal to [Int] number of kiloliters.\n */\npublic inline val Int.kiloliters: Volume get() = toVolume(VolumeUnit.Metric.Kiloliter)\n\n/**\n * Returns a [Volume] equal to [Long] number of kiloliters.\n */\npublic inline val Long.kiloliters: Volume get() = toVolume(VolumeUnit.Metric.Kiloliter)\n\n/**\n * Returns a [Volume] equal to [Double] number of kiloliters. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.kiloliters: Volume get() = toVolume(VolumeUnit.Metric.Kiloliter)\n\n/**\n * Returns a [Volume] equal to [Int] number of megaliters.\n */\npublic inline val Int.megaliters: Volume get() = toVolume(VolumeUnit.Metric.Megaliter)\n\n/**\n * Returns a [Volume] equal to [Long] number of megaliters.\n */\npublic inline val Long.megaliters: Volume get() = toVolume(VolumeUnit.Metric.Megaliter)\n\n/**\n * Returns a [Volume] equal to [Double] number of megaliters. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.megaliters: Volume get() = toVolume(VolumeUnit.Metric.Megaliter)\n\n/**\n * Returns a [Volume] equal to [Int] number of gigaliters.\n */\npublic inline val Int.gigaliters: Volume get() = toVolume(VolumeUnit.Metric.Gigaliter)\n\n/**\n * Returns a [Volume] equal to [Long] number of gigaliters.\n */\npublic inline val Long.gigaliters: Volume get() = toVolume(VolumeUnit.Metric.Gigaliter)\n\n/**\n * Returns a [Volume] equal to [Double] number of gigaliters. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.gigaliters: Volume get() = toVolume(VolumeUnit.Metric.Gigaliter)\n\n/**\n * Returns a [Volume] equal to [Int] number of teraliters.\n */\npublic inline val Int.teraliters: Volume get() = toVolume(VolumeUnit.Metric.Teraliter)\n\n/**\n * Returns a [Volume] equal to [Long] number of teraliters.\n */\npublic inline val Long.teraliters: Volume get() = toVolume(VolumeUnit.Metric.Teraliter)\n\n/**\n * Returns a [Volume] equal to [Double] number of teraliters. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.teraliters: Volume get() = toVolume(VolumeUnit.Metric.Teraliter)\n\n/**\n * Returns a [Volume] equal to [Int] number of petaliters.\n */\npublic inline val Int.petaliters: Volume get() = toVolume(VolumeUnit.Metric.Petaliter)\n\n/**\n * Returns a [Volume] equal to [Long] number of petaliters.\n */\npublic inline val Long.petaliters: Volume get() = toVolume(VolumeUnit.Metric.Petaliter)\n\n/**\n * Returns a [Volume] equal to [Double] number of petaliters. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic inline val Double.petaliters: Volume get() = toVolume(VolumeUnit.Metric.Petaliter)\n\n/**\n * Returns a [Volume] equal to [Int] number of the specified [unit].\n */\npublic fun Int.toVolume(unit: VolumeUnit): Volume = toLong().toVolume(unit)\n\n/**\n * Returns a [Volume] equal to [Long] number of the specified [unit].\n */\npublic fun Long.toVolume(unit: VolumeUnit): Volume {\n    return Volume(saturated * unit.cubicCentimetersScale)\n}\n\n/**\n * Returns a [Volume] equal to [Double] number of the specified [unit]. Depending on its magnitude, some precision may\n * be lost.\n *\n * @throws IllegalArgumentException is this [Double] is [Double.NaN].\n */\npublic fun Double.toVolume(unit: VolumeUnit): Volume {\n    val valueInCubicCentis = this * unit.cubicCentimetersScale\n    require(!valueInCubicCentis.isNaN()) { \"Volume value cannot be NaN.\" }\n    return Volume(valueInCubicCentis.roundToLong().saturated)\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Acceleration.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.secondScale\nimport io.github.kevincianfarini.alchemist.internal.shortNameSquared\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.scalar.nmPerSecond\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport kotlin.jvm.JvmInline\nimport kotlin.math.roundToLong\nimport kotlin.text.Typography.nbsp\nimport kotlin.time.Duration\nimport kotlin.time.DurationUnit\n\n/**\n * Represents a measure of acceleration and is capable of storing ±9.2 billion m/s² at nm/s² precision.\n */\n@JvmInline\npublic value class Acceleration internal constructor(\n    private val rawNanometersPerSecondSquared: SaturatingLong\n) : Comparable<Acceleration> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the resulting [Velocity] after multiplying this acceleration by the specified [duration].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of this acceleration or the\n     * specified [duration] some precision may be lost.\n     *\n     * @throws IllegalArgumentException if this acceleration is infinite and [duration] is zero, or if this acceleration\n     * is zero and [duration] is infinite.\n     */\n    public operator fun times(duration: Duration): Velocity = duration.toComponents { seconds, nanoseconds ->\n        val secondComponent = (rawNanometersPerSecondSquared * seconds).nmPerSecond\n        val preciseNanosecondComponent = ((rawNanometersPerSecondSquared * nanoseconds) / 1_000_000_000).nmPerSecond\n        if (preciseNanosecondComponent.isFinite()) {\n            secondComponent + preciseNanosecondComponent\n        } else {\n            val coarseNanosecondComponent = ((rawNanometersPerSecondSquared / 1_000_000_000) * nanoseconds).nmPerSecond\n            secondComponent + coarseNanosecondComponent\n        }\n    }\n\n    /**\n     * Returns the resulting [Force] after multiplying this acceleration by the specified [mass].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of this acceleration or the\n     * specified [mass] some precision may be lost.\n     *\n     * @throws IllegalArgumentException if this acceleration is infinite and [mass] is zero, or if this acceleration\n     * is zero and [mass] is infinite.\n     */\n    public operator fun times(mass: Mass): Force {\n        return mass.toInternationalComponents { tera, giga, mega, kilo, grams, milli, micro ->\n            // Try to find the right level which we can perform this operation at without losing precision.\n            // --------------------------------------------------------------------------------------------\n            // 1 nm/s² * 1 microgram is 1 attonewton.\n            // 1 nm/s² * 1 milligram is 1 femtonewton.\n            // 1 nm/s² * 1 gram is 1 piconewton.\n            // 1 nm/s² * 1 kilogram is 1 nanonewton.\n            // 1 nm/s² * 1 megagram is 1 micronewton.\n            // 1 nm/s² * 1 gigagram is 1 millinewton.\n            // 1 nm/s² * 1 teragram is 1 newton.\n            // --------------------------------------------------------------------------------------------\n            val newtons = rawNanometersPerSecondSquared * tera\n            val millinewtons = rawNanometersPerSecondSquared * giga\n            val micronewtons = rawNanometersPerSecondSquared * mega\n            val nanonewtons = rawNanometersPerSecondSquared * kilo\n            val piconewtons = rawNanometersPerSecondSquared * grams\n            val femtonewtons = rawNanometersPerSecondSquared * milli\n            val attonewtons = rawNanometersPerSecondSquared * micro\n            // ----------- Try attonewton precision. ------------------------------------------------------\n            val attoN = attonewtons + (femtonewtons * 1_000) + (piconewtons * 1_000_000) + (nanonewtons * 1_000_000_000) + (micronewtons * 1_000_000_000_000) + (millinewtons * 1_000_000_000_000_000) + (newtons * 1_000_000_000_000_000_000)\n            if (attoN.isFinite()) return@toInternationalComponents Force(attoN / 1_000_000_000)\n            // ----------- Try femtonewton precision. ------------------------------------------------------\n            val femtoN = (attonewtons / 1_000) + femtonewtons + (piconewtons * 1_000) + (nanonewtons * 1_000_000) + (micronewtons * 1_000_000_000) + (millinewtons * 1_000_000_000_000) + (newtons * 1_000_000_000_000_000)\n            if (femtoN.isFinite()) return@toInternationalComponents Force(femtoN / 1_000_000)\n            // ----------- Try piconewton precision. ------------------------------------------------------\n            val picoN = (attonewtons / 1_000_000) + (femtonewtons / 1_000) + piconewtons + (nanonewtons * 1_000) + (micronewtons * 1_000_000) + (millinewtons * 1_000_000_000) + (newtons * 1_000_000_000_000)\n            if (picoN.isFinite()) return@toInternationalComponents Force(picoN / 1_000)\n            // ----------- Default nanonewton precision. --------------------------------------------------\n            val nanoN = (attonewtons / 1_000_000_000) + (femtonewtons / 1_000_000) + (piconewtons / 1_000) + nanonewtons + (micronewtons * 1_000) + (millinewtons * 1_000_000) + (newtons * 1_000_000_000)\n            Force(nanoN)\n        }\n    }\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns the number that is the ratio of this and the [other] acceleration value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] acceleration are [infinite][isInfinite].\n     */\n    public operator fun div(other: Acceleration): Double {\n        return rawNanometersPerSecondSquared.toDouble() / other.rawNanometersPerSecondSquared.toDouble()\n    }\n\n    /**\n     * Returns an acceleration whose value is this acceleration value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Acceleration = div(scale.toLong())\n\n    /**\n     * Returns an acceleration whose value is this acceleration value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * acceleration is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Acceleration = Acceleration(rawNanometersPerSecondSquared / scale)\n\n    /**\n     * Returns the negative of this acceleration value.\n     */\n    public operator fun unaryMinus(): Acceleration = Acceleration(-rawNanometersPerSecondSquared)\n\n    /**\n     * Returns an acceleration whose value is the difference between this and the [other] acceleration value.\n     *\n     * @throws IllegalArgumentException if this acceleration and the [other] acceleration are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Acceleration): Acceleration {\n        return Acceleration(rawNanometersPerSecondSquared - other.rawNanometersPerSecondSquared)\n    }\n\n    /**\n     * Returns an acceleration whose value is the sum between this and the [other] acceleration value.\n     *\n     * @throws IllegalArgumentException if this acceleration and the [other] acceleration are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Acceleration): Acceleration {\n        return Acceleration(rawNanometersPerSecondSquared + other.rawNanometersPerSecondSquared)\n    }\n\n    /**\n     * Returns an acceleration whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this acceleration is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Acceleration = times(scale.toLong())\n\n    /**\n     * Returns an acceleration whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this acceleration is [infinite][isInfinite] and [scale] is 0, or when this\n     * acceleration is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Acceleration = Acceleration(rawNanometersPerSecondSquared * scale)\n\n    /**\n     * Returns an acceleration whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this acceleration is [infinite][isInfinite] and [scale] is 0.0 or when this acceleration is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Acceleration = Acceleration(rawNanometersPerSecondSquared * scale)\n\n    /**\n     * Returns an acceleration whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this acceleration is [infinite][isInfinite] and [scale] is 0.0 or when this acceleration is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Acceleration = Acceleration(rawNanometersPerSecondSquared / scale)\n\n    // endregion\n\n    // region Acceleration to Scalar Conversions\n\n    /**\n     * Returns the value of this acceleration expressed as a [Long] number of the specified [lengthUnit] per\n     * [durationUnit]². Infinite values are converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its\n     * sign.\n     */\n    public fun toLong(lengthUnit: LengthUnit, durationUnit: DurationUnit): Long {\n        return toDouble(lengthUnit, durationUnit).roundToLong()\n    }\n\n    /**\n     * Returns the value of this acceleration expressed as a [Double] number of the specified [lengthUnit] per\n     * [durationUnit]². Infinite values are converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY]\n     * depending on its sign.\n     */\n    public fun toDouble(lengthUnit: LengthUnit, durationUnit: DurationUnit): Double {\n        val lengthPerSecond2 = rawNanometersPerSecondSquared.toDouble() / lengthUnit.nanometerScale.toDouble()\n        return lengthPerSecond2 * durationUnit.secondScale * durationUnit.secondScale\n    }\n\n    /**\n     * Returns a fractional string representation of this acceleration expressed in the specified [lengthUnit] per\n     * [durationUnit]² and is rounded to the specified [decimals].\n     */\n    public fun toString(lengthUnit: LengthUnit, durationUnit: DurationUnit, decimals: Int = 0): String {\n        return when (isInfinite()) {\n            true -> rawNanometersPerSecondSquared.toString()\n            false -> buildString {\n                append(toDouble(lengthUnit, durationUnit).toDecimalString(decimals))\n                append(nbsp)\n                append(lengthUnit.symbol)\n                append(\"/\")\n                append(durationUnit.shortNameSquared)\n            }\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this acceleration expressed in the largest\n     * [LengthUnit.International] per second² quantity which is greater than or equal to 1.\n     */\n    override fun toString(): String {\n        val lengthUnit = LengthUnit.International.entries.asReversed().firstOrNull { unit ->\n            rawNanometersPerSecondSquared.absoluteValue / unit.nanometerScale > 0\n        }\n        return toString(lengthUnit ?: LengthUnit.International.Nanometer, DurationUnit.SECONDS, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this acceleration value is finite.\n     */\n    public fun isFinite(): Boolean = rawNanometersPerSecondSquared.isFinite()\n\n    /**\n     * Returns true if this acceleration value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawNanometersPerSecondSquared.isInfinite()\n\n    /**\n     * Compares this acceleration with the [other] acceleration. Returns zero if this acceleration is equal\n     * to the specified [other] acceleration, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    override fun compareTo(other: Acceleration): Int {\n        return rawNanometersPerSecondSquared.compareTo(other.rawNanometersPerSecondSquared)\n    }\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Area.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.scalar.mm2\nimport io.github.kevincianfarini.alchemist.unit.AreaUnit\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport kotlin.jvm.JvmInline\nimport kotlin.math.pow\n\n/**\n * Represents a measure of area and is capable of storing ±9.22 million kilometers² at millimeter² precision.\n */\n@JvmInline\npublic value class Area internal constructor(internal val rawMillimetersSquared: SaturatingLong) : Comparable<Area> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the resulting length after dividing this area by the specified [length].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of this area some precision\n     * may be lost.\n     *\n     * @throws IllegalArgumentException if both this area and [length] are infinite.\n     */\n    public operator fun div(length: Length): Length {\n        // Try to find the right level which we can perform this operation at without losing precision.\n        // --------------------------------------------------------------------------------------------\n        // 1 nanometer² / 1 nanometer is 1 nanometer\n        // 1 micrometer² / 1 nanometer is 1,000,000 nanometers.\n        // 1 millimeter² / 1 nanometer is 1,000,000,000,000 nanometers.\n        // --------------------------------------------------------------------------------------------\n        val nano2 = rawMillimetersSquared * 1_000_000_000_000\n        if (nano2.isFinite()) return Length(nano2 / length.rawNanometers)\n        val micro2 = rawMillimetersSquared * 1_000_000\n        if (micro2.isFinite()) return Length((micro2 / length.rawNanometers) * 1_000_000)\n        return Length((rawMillimetersSquared / length.rawNanometers) * 1_000_000_000_000)\n    }\n\n    /**\n     * Returns the resulting [Volume] after applying this area over the specified [length].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of this area or the\n     * specified [length] some precision may be lost.\n     *\n     * @throws IllegalArgumentException if this area is [infinite][isInfinite] and [length] is zero, or if this area\n     * is zero and [length] is infinite.\n     */\n    public operator fun times(length: Length): Volume {\n        return length.toInternationalComponents { giga, mega, kilo, meters, centi, milli, micro, nano ->\n            // Try to find the right level which we can perform this operation at without losing precision.\n            // --------------------------------------------------------------------------------------------\n            // 1 millimeter² * 1 nm is 1 picoliter.\n            // 1 millimeter² * 1 μm is 1 nanoliter.\n            // 1 millimeter² * 1 mm is 1 microliter.\n            // 1 millimeter² * 1 cm is 10 microliters.\n            // 1 millimeter² * 1 m is 1 milliliter.\n            // 1 millimeter² * 1 km is 1 liter.\n            // 1 millimeter² * 1 Mm is 1 kiloliter.\n            // 1 millimeter² * 1 Gm is 1 megaliter.\n            // --------------------------------------------------------------------------------------------\n            val megaliters = rawMillimetersSquared * giga\n            val kiloliters = rawMillimetersSquared * mega\n            val liters = rawMillimetersSquared * kilo\n            val milliliters = rawMillimetersSquared * meters\n            val microliters = (rawMillimetersSquared * centi * 10) + (rawMillimetersSquared * milli)\n            val nanoliters = rawMillimetersSquared * micro\n            val picoliters = rawMillimetersSquared * nano\n            // ----------- Try picoliter precision. ------------------------------------------------------\n            val picoL = picoliters + (nanoliters * 1_000) + (microliters * 1_000_000) + (milliliters * 1_000_000_000) + (liters * 1_000_000_000_000) + (kiloliters * 1_000_000_000_000_000) + (megaliters * 1_000_000_000_000_000_000)\n            if (picoL.isFinite()) return@toInternationalComponents Volume(picoL / 1_000_000_000)\n            // ----------- Try nanoliter precision. ------------------------------------------------------\n            val nanoL = (picoliters / 1_000) + nanoliters + (microliters * 1_000) + (milliliters * 1_000_000) + (liters * 1_000_000_000) + (kiloliters * 1_000_000_000_000) + (megaliters * 1_000_000_000_000_000)\n            if (nanoL.isFinite()) return@toInternationalComponents Volume(nanoL / 1_000_000)\n            // ----------- Try microliter precision. -----------------------------------------------------\n            val microL = (picoliters / 1_000_000) + (nanoliters / 1_000) + microliters + (milliliters * 1_000) + (liters * 1_000_000) + (kiloliters * 1_000_000_000) + (megaliters * 1_000_000_000_000)\n            if (microL.isFinite()) return@toInternationalComponents Volume(microL / 1_000)\n            // ----------- Default milliliter precision. -------------------------------------------------\n            val milliL = (picoliters / 1_000_000_000) + (nanoliters / 1_000_000) + (microliters / 1_000) + milliliters + (liters * 1_000) + (kiloliters * 1_000_000) + (megaliters * 1_000_000_000)\n            Volume(milliL)\n        }\n    }\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns an area whose value is this area value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Area = div(scale.toLong())\n\n    /**\n     * Returns an area whose value is this area value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * area is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Area = Area(rawMillimetersSquared / scale)\n\n    /**\n     * Returns the number that is the ratio of this and the [other] area value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] area are [infinite][isInfinite].\n     */\n    public operator fun div(other: Area): Double {\n        return rawMillimetersSquared.toDouble() / other.rawMillimetersSquared.toDouble()\n    }\n\n    /**\n     * Returns the negative of this area value.\n     */\n    public operator fun unaryMinus(): Area = Area(-rawMillimetersSquared)\n\n    /**\n     * Returns an area whose value is the difference between this and the [other] area value.\n     *\n     * @throws IllegalArgumentException if this area and the [other] area are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Area): Area = Area(rawMillimetersSquared - other.rawMillimetersSquared)\n\n    /**\n     * Returns an area whose value is the sum between this and the [other] area value.\n     *\n     * @throws IllegalArgumentException if this area and the [other] area are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Area): Area = Area(rawMillimetersSquared + other.rawMillimetersSquared)\n\n    /**\n     * Returns an area whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this area is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Area = times(scale.toLong())\n\n    /**\n     * Returns an area whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this area is [infinite][isInfinite] and [scale] is 0, or when this\n     * area is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Area = Area(rawMillimetersSquared * scale)\n\n    /**\n     * Returns an area whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this area is [infinite][isInfinite] and [scale] is 0.0 or when this area is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Area = Area(rawMillimetersSquared * scale)\n\n    /**\n     * Returns an area whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this area is [infinite][isInfinite] and [scale] is 0.0 or when this area is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Area = Area(rawMillimetersSquared / scale)\n\n    // endregion\n\n    // region Area to Scalar Conversions\n\n    /**\n     * Splits this area into megameters², kilometers², meters², centimeters², and millimeters² and executes the [action]\n     * with those components. The result of [action] is returned as the result of this function.\n     *\n     * Infinite areas invoke [action] with [Long.MAX_VALUE] or [Long.MIN_VALUE] for every component, depending on the\n     * infinite value's sign.\n     */\n    public fun <T> toInternationalComponents(\n        action: (\n            megametersSquared: Long,\n            kilometersSquared: Long,\n            metersSquared: Long,\n            centimetersSquared: Long,\n            millimetersSquared: Long,\n        ) -> T,\n    ): T {\n        val mega = rawMillimetersSquared / 1_000_000_000_000_000_000\n        val megaRemainder = rawMillimetersSquared % 1_000_000_000_000_000_000\n        val kilo = megaRemainder / 1_000_000_000_000\n        val kiloRemainder = megaRemainder % 1_000_000_000_000\n        val meters = kiloRemainder / 1_000_000\n        val metersRemainder = kiloRemainder % 1_000_000\n        val centi = metersRemainder / 100\n        val milli = metersRemainder % 100\n        return action(mega.rawValue, kilo.rawValue, meters.rawValue, centi.rawValue, milli.rawValue)\n    }\n\n    /**\n     * Returns the value of this area expressed as a [Long] number of the specified [unit]. Infinite values are\n     * converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(unit: AreaUnit): Long {\n        return (rawMillimetersSquared / unit.millimetersSquaredScale).rawValue\n    }\n\n    /**\n     * Returns the value of this area expressed as a [Long] number of the specified [squareUnit]². Infinite\n     * values are converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(squareUnit: LengthUnit): Long {\n        val nm2 = rawMillimetersSquared * 1_000_000_000_000\n        val nm2Scale = squareUnit.nanometerScale.saturated * squareUnit.nanometerScale\n        return if (nm2.isFinite() && nm2Scale.isFinite()) {\n            nm2 / nm2Scale\n        } else {\n            val um2 = rawMillimetersSquared * 1_000_000\n            val um2Unit = squareUnit.nanometerScale.saturated / 1_000\n            val um2Scale = um2Unit * um2Unit\n            if (um2.isFinite() && um2Scale.isFinite()) {\n                um2 / um2Scale\n            } else {\n                val mm2Unit = squareUnit.nanometerScale.saturated / 1_000_000\n                val mm2Scale = mm2Unit * mm2Unit\n                if (rawMillimetersSquared.isFinite() && mm2Scale.isFinite()) {\n                    rawMillimetersSquared / mm2Scale\n                } else {\n                    0L.saturated\n                }\n            }\n        }.rawValue\n    }\n\n    /**\n     * Returns the value of this area expressed as a [Double] number of the specified [unit]. Infinite values are\n     * converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] depending on its sign.\n     */\n    public fun toDouble(unit: AreaUnit): Double {\n        return this / unit.millimetersSquaredScale.mm2\n    }\n\n    /**\n     * Returns the value of this area expressed as a [Double] number of the specific [LengthUnit]². Infinite values are\n     * converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] depending on its sign.\n     */\n    public fun toDouble(squareUnit: LengthUnit): Double {\n        val nm2 = rawMillimetersSquared.toDouble() * 1_000_000_000_000\n        return nm2 / squareUnit.nanometerScale.toDouble().pow(2)\n    }\n\n    /**\n     * Returns a fractional string representation of this area expressed in the specified [AreaUnit] and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(unit: AreaUnit, decimals: Int = 0): String = when (isInfinite()) {\n        true -> rawMillimetersSquared.toString()\n        false -> buildString {\n            append(toDouble(unit).toDecimalString(decimals))\n            append(unit.symbol)\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this area expressed in the specified [LengthUnit]² and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(squareUnit: LengthUnit, decimals: Int = 0): String = when (isInfinite()) {\n        true -> rawMillimetersSquared.toString()\n        false -> buildString {\n            append(toDouble(squareUnit).toDecimalString(decimals))\n            append(squareUnit.symbol)\n            append(\"²\")\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this area expressed in the largest [LengthUnit]² quantity which is\n     * greater than or equal to 1.\n     */\n    public override fun toString(): String {\n        val largestUnit = LengthUnit.International.entries.asReversed().firstOrNull { squareUnit ->\n            toDouble(squareUnit) >= 1.0\n        }\n        return toString(largestUnit ?: LengthUnit.International.Millimeter, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this area value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawMillimetersSquared.isInfinite()\n\n    /**\n     * Returns true if this area value is finite.\n     */\n    public fun isFinite(): Boolean = rawMillimetersSquared.isFinite()\n\n    /**\n     * Compares this area with the [other] area. Returns zero if this area is equal\n     * to the specified [other] area, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    public override fun compareTo(other: Area): Int {\n        return rawMillimetersSquared.compareTo(other.rawMillimetersSquared)\n    }\n\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Energy.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.isPreciseToNanosecond\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.internal.throwIllegalArgumentException\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.scalar.kilonewtons\nimport io.github.kevincianfarini.alchemist.scalar.kilowatts\nimport io.github.kevincianfarini.alchemist.scalar.megawatts\nimport io.github.kevincianfarini.alchemist.scalar.microwatts\nimport io.github.kevincianfarini.alchemist.scalar.millijoules\nimport io.github.kevincianfarini.alchemist.scalar.milliwatts\nimport io.github.kevincianfarini.alchemist.scalar.watts\nimport io.github.kevincianfarini.alchemist.unit.EnergyUnit\nimport kotlin.jvm.JvmInline\nimport kotlin.time.Duration\n\n/**\n * Represents an amount of energy and is capable of storing ±9.2 petajoules or ±2.56 terawatt-hours at millijoule\n * precision.\n */\n@JvmInline\npublic value class Energy internal constructor(private val rawMillijoules: SaturatingLong) : Comparable<Energy> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the constant [Force] applied over the specified [length] required to expend this amount of energy.\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this energy or the\n     * other [length], some precision may be lost.\n     *\n     * @throws IllegalArgumentException if both this energy and [length] are infinite.\n     */\n    public operator fun div(length: Length): Force {\n        // TODO This is simplistic and we should attempt to retain precision in the future.\n        return ((rawMillijoules / length.rawNanometers) * 1_000).kilonewtons\n    }\n\n    /**\n     * Returns the constant [Power] applied over the specified [duration] to generate this amount of energy.\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this energy or the\n     * other [duration], some precision may be lost.\n     *\n     * @throws IllegalArgumentException if both this energy and [duration] are infinite.\n     */\n    public operator fun div(duration: Duration): Power = when {\n        rawMillijoules.isInfinite() && duration.isInfinite() -> {\n            throwIllegalArgumentException(\"Dividing two infinite values yields an undefined result.\")\n        }\n        rawMillijoules.isInfinite() -> Power(rawMillijoules)\n        duration.isInfinite() -> Power(0L.saturated)\n        else -> calculatePower(duration)\n    }\n\n    private fun calculatePower(duration: Duration): Power {\n        // Try to find the right level which we can perform this operation at without losing precision.\n        if (duration.isPreciseToNanosecond()) {\n            val power = millijoulesPerNs(rawMillijoules, duration.inWholeNanoseconds)\n            if (power.isFinite()) return power\n        }\n        val ms = duration.inWholeMilliseconds\n        val power = millijoulesPerMs(rawMillijoules, ms)\n        if (power.isFinite()) return power\n        return (rawMillijoules / ms).watts\n    }\n\n    private fun millijoulesPerMs(millijoules: SaturatingLong, ms: Long): Power {\n        // 1 millijoule per 1 millisecond is 1 watt.\n        val watts = (millijoules / ms).watts\n        return watts + microjoulesPerMs((millijoules % ms) * 1_000, ms)\n    }\n\n    private fun microjoulesPerMs(microjoules: SaturatingLong, ms: Long): Power {\n        // 1 microjoule per 1 millisecond is 1 milliwatt.\n        val milliwatts = (microjoules / ms).milliwatts\n        return milliwatts + nanojoulesPerMs((microjoules % ms) * 1_000, ms)\n    }\n\n    private fun nanojoulesPerMs(nanojoules: SaturatingLong, ms: Long): Power {\n        // 1 nanojoule per 1 millisecond is 1 microwatt.\n        return (nanojoules / ms).microwatts\n    }\n\n    private fun millijoulesPerNs(millijoules: SaturatingLong, ns: Long): Power {\n        // 1 millijoule per 1 nanosecond is 1 megawatt.\n        val megawatts = (millijoules / ns).megawatts\n        return megawatts + microjoulesPerNs((millijoules % ns) * 1_000, ns)\n    }\n\n    private fun microjoulesPerNs(microjoules: SaturatingLong, ns: Long): Power {\n        // 1 microjoule per 1 nanosecond is 1 kilowatt.\n        val kilowatts = (microjoules / ns).kilowatts\n        return kilowatts + nanojoulesPerNs((microjoules % ns) * 1_000, ns)\n    }\n\n    private fun nanojoulesPerNs(nanojoules: SaturatingLong, ns: Long): Power {\n        // 1 nanojoule per 1 nanosecond is 1 watt.\n        val watts = (nanojoules / ns).watts\n        return watts + picojoulesPerNs((nanojoules % ns) * 1_000, ns)\n    }\n\n    private fun picojoulesPerNs(picojoules: SaturatingLong, ns: Long): Power {\n        // 1 picojoule per 1 nanosecond is 1 milliwatt.\n        val milliwatts = (picojoules / ns).milliwatts\n        return milliwatts + femtojoulesPerNs((picojoules % ns) * 1_000, ns)\n    }\n\n    private fun femtojoulesPerNs(femtojoules: SaturatingLong, ns: Long): Power {\n        // 1 femtojoule per 1 nanosecond is 1 microwatt.\n        return (femtojoules / ns).microwatts\n    }\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns the number that is the ratio of this and the [other] energy value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] energy are [infinite][isInfinite].\n     */\n    public operator fun div(other: Energy): Double {\n        return rawMillijoules.toDouble() / other.rawMillijoules.toDouble()\n    }\n\n    /**\n     * Returns an energy whose value is this energy value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Energy {\n        return div(scale.toLong())\n    }\n\n    /**\n     * Returns an energy whose value is this energy value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * energy is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Energy {\n        return Energy(rawMillijoules / scale)\n    }\n\n    /**\n     * Returns the negative of this energy value.\n     */\n    public operator fun unaryMinus(): Energy = Energy(-rawMillijoules)\n\n    /**\n     * Returns an energy whose value is the difference between this and the [other] energy value.\n     *\n     * @throws IllegalArgumentException if this energy and the [other] energy are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Energy): Energy {\n        return Energy(rawMillijoules - other.rawMillijoules)\n    }\n\n    /**\n     * Returns an energy whose value is the sum between this and the [other] energy value.\n     *\n     * @throws IllegalArgumentException if this energy and the [other] energy are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Energy): Energy {\n        return Energy(rawMillijoules + other.rawMillijoules)\n    }\n\n    /**\n     * Returns an energy whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this energy is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Energy {\n        return div(scale.toLong())\n    }\n\n    /**\n     * Returns an energy whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this energy is [infinite][isInfinite] and [scale] is 0, or when this\n     * energy is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Energy {\n        return Energy(rawMillijoules * scale)\n    }\n\n    /**\n     * Returns an energy whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this energy is [infinite][isInfinite] and [scale] is 0.0 or when this energy is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Energy = Energy(rawMillijoules * scale)\n\n    /**\n     * Returns an energy whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this energy is [infinite][isInfinite] and [scale] is 0.0 or when this energy is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Energy = Energy(rawMillijoules / scale)\n\n    // endregion\n\n    // region Energy to Scalar Conversions\n\n    /**\n     * Splits this energy into petajoules, tetrajoules, gigajoules, megajoules, kilojoules, joules, and millijoules\n     * and executes the [action] with those components. The result of [action] is returned as the result of this\n     * function.\n     *\n     * Infinite energy values invoke [action] with [Long.MAX_VALUE] or [Long.MIN_VALUE] for every component, depending\n     * on the infinite value's sign.\n     */\n    public fun <T> toInternationalComponents(\n        action: (\n            petajoules: Long,\n            tetrajoules: Long,\n            gigajoules: Long,\n            megajoules: Long,\n            kilojoules: Long,\n            joules: Long,\n            millijoules: Long,\n        ) -> T\n    ): T {\n        val peta = rawMillijoules / EnergyUnit.International.Petajoule.millijouleScale\n        val petaRemainder = rawMillijoules % EnergyUnit.International.Petajoule.millijouleScale\n        val tetra = petaRemainder / EnergyUnit.International.Tetrajoule.millijouleScale\n        val tetraRemainder = petaRemainder % EnergyUnit.International.Tetrajoule.millijouleScale\n        val giga = tetraRemainder / EnergyUnit.International.Gigajoule.millijouleScale\n        val gigaRemainder = tetraRemainder % EnergyUnit.International.Gigajoule.millijouleScale\n        val mega = gigaRemainder / EnergyUnit.International.Megajoule.millijouleScale\n        val megaRemainder = gigaRemainder % EnergyUnit.International.Megajoule.millijouleScale\n        val kilo = megaRemainder / EnergyUnit.International.Kilojoule.millijouleScale\n        val kiloRemainder = megaRemainder % EnergyUnit.International.Kilojoule.millijouleScale\n        val joule = kiloRemainder / EnergyUnit.International.Joule.millijouleScale\n        val milliJoule = kiloRemainder % EnergyUnit.International.Joule.millijouleScale\n        return action(\n            peta.rawValue,\n            tetra.rawValue,\n            giga.rawValue,\n            mega.rawValue,\n            kilo.rawValue,\n            joule.rawValue,\n            milliJoule.rawValue,\n        )\n    }\n\n    /**\n     * Splits this energy into terawatt-hours, gigawatt-hours, megawatt-hours, kilowatt-hours, watt-hours,\n     * milliwatt-hours, and microwatt-hours and executes the [action] with those components. The result of [action] is\n     * returned as the result of this function.\n     *\n     * Infinite energy values invoke [action] with [Long.MAX_VALUE], [Long.MIN_VALUE], [Double.POSITIVE_INFINITY], or\n     * [Double.NEGATIVE_INFINITY] for every component, depending on the infinite value's sign and the component's type.\n     */\n    public fun <T> toElectricityComponents(\n        action: (\n            terawattHours: Long,\n            gigawattHours: Long,\n            megawattHours: Long,\n            kilowattHours: Long,\n            wattHours: Long,\n            milliwattHours: Long,\n            microwattHours: Double,\n        ) -> T\n    ): T {\n        val tera = rawMillijoules / EnergyUnit.Electricity.TerawattHour.millijouleScale\n        val teraRemainder = rawMillijoules % EnergyUnit.Electricity.TerawattHour.millijouleScale\n        val giga = teraRemainder / EnergyUnit.Electricity.GigawattHour.millijouleScale\n        val gigaRemainder = teraRemainder % EnergyUnit.Electricity.GigawattHour.millijouleScale\n        val mega = gigaRemainder / EnergyUnit.Electricity.MegawattHour.millijouleScale\n        val megaRemainder = gigaRemainder % EnergyUnit.Electricity.MegawattHour.millijouleScale\n        val kilo = megaRemainder / EnergyUnit.Electricity.KilowattHour.millijouleScale\n        val kiloRemainder = megaRemainder % EnergyUnit.Electricity.KilowattHour.millijouleScale\n        val wattHour = kiloRemainder / EnergyUnit.Electricity.WattHour.millijouleScale\n        val wattRemainder = kiloRemainder % EnergyUnit.Electricity.WattHour.millijouleScale\n        val milliwattHour = wattRemainder / EnergyUnit.Electricity.MilliwattHour.millijouleScale\n        val milliwattRemainder = wattRemainder % EnergyUnit.Electricity.MilliwattHour.millijouleScale\n        val microwattHours = milliwattRemainder.toDouble() / 3.6 // 3.6 millijoules per microwatt-hour.\n        return action(\n            tera.rawValue,\n            giga.rawValue,\n            mega.rawValue,\n            kilo.rawValue,\n            wattHour.rawValue,\n            milliwattHour.rawValue,\n            microwattHours,\n        )\n    }\n\n    /**\n     * Returns the value of this energy expressed as a [Long] number of the specified [unit]. Infinite values are\n     * converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(unit: EnergyUnit): Long {\n        return (rawMillijoules / unit.millijouleScale).rawValue\n    }\n\n    /**\n     * Returns the value of this energy expressed as a [Double] number of the specified [unit]. Infinite values are\n     * converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] depending on its sign.\n     */\n    public fun toDouble(unit: EnergyUnit): Double {\n        return this / unit.millijouleScale.millijoules\n    }\n\n    /**\n     * Returns a fractional string representation of this energy expressed in the specified [EnergyUnit] and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(unit: EnergyUnit, decimals: Int = 0): String = when (rawMillijoules.isInfinite()) {\n        true -> rawMillijoules.toString()\n        false -> buildString {\n            append(toDouble(unit).toDecimalString(decimals))\n            append(unit.symbol)\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this energy expressed in the largest [EnergyUnit.International]\n     * quantity which is greater than or equal to 1.\n     */\n    public override fun toString(): String {\n        val largestUnit = EnergyUnit.International.entries.asReversed().firstOrNull { unit ->\n            rawMillijoules.absoluteValue / unit.millijouleScale > 0\n        }\n        return toString(largestUnit ?: EnergyUnit.International.Millijoule, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this area value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawMillijoules.isInfinite()\n\n    /**\n     * Returns true if this area value is finite.\n     */\n    public fun isFinite(): Boolean = rawMillijoules.isFinite()\n\n    /**\n     * Compares this energy with the [other] energy. Returns zero if this energy is equal\n     * to the specified [other] energy, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    public override fun compareTo(other: Energy): Int {\n        return rawMillijoules.compareTo(other.rawMillijoules)\n    }\n\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Force.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.scalar.nmPerSecond2\nimport io.github.kevincianfarini.alchemist.unit.ForceUnit\nimport kotlin.jvm.JvmInline\n\n/**\n * Represents a measure of force and is capable of storing ±9.2 billion newtons at nanonewton precision.\n */\n@JvmInline\npublic value class Force internal constructor(private val rawNanonewtons: SaturatingLong) : Comparable<Force> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the resulting [Acceleration] from applying this force to the specified [mass].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this force or the\n     * other [mass], some precision may be lost.\n     *\n     * @throws IllegalArgumentException if both this force and [mass] are infinite.\n     */\n    public operator fun div(mass: Mass): Acceleration {\n        // 1 nanonewton / 1 microgram is 100 centimeters/second².\n        // TODO This is simplistic and we should attempt to retain precision in the future.\n        return ((rawNanonewtons / mass.rawMicrograms) * 1_000_000_000).nmPerSecond2\n    }\n\n    /**\n     * Returns the amount of [Energy] required to apply this force over the specified [length].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this force or the\n     * specified [length], some precision may be lost.\n     *\n     * @throws IllegalArgumentException when [length] is [infinite][isInfinite] and this force is 0 or vice versa.\n     */\n    public operator fun times(length: Length): Energy {\n        return length.toInternationalComponents { giga, mega, kilo, meters, centi, milli, micro, nano ->\n            // Try to find the right level which we can perform this operation at without losing precision.\n            // --------------------------------------------------------------------------------------------\n            // 1 nN * 1 nm is 1 attojoule.\n            // 1 nN * 1 μm is 1 femtojoule.\n            // 1 nN * 1 mm is 1 picojoule.\n            // 1 nN * 1 cm is 10 picojoules.\n            // 1 nN * 1 m is 1 nanojoule.\n            // 1 nN * 1 km is 1 microjoule.\n            // 1 nN * 1 Mm is 1 millijoule.\n            // 1 nN * 1 Gm is 1 joule.\n            // --------------------------------------------------------------------------------------------\n            val joules = rawNanonewtons * giga\n            val millijoules = rawNanonewtons * mega\n            val microjoules = rawNanonewtons * kilo\n            val nanojoules = rawNanonewtons * meters\n            val picojoules = (rawNanonewtons * centi * 10) + (rawNanonewtons * milli)\n            val femtojoules = rawNanonewtons * micro\n            val attojoules = rawNanonewtons * nano\n            // ----------- Try attojoule precision. ------------------------------------------------------\n            val attoJ = attojoules + (femtojoules * 1_000) + (picojoules * 1_000_000) + (nanojoules * 1_000_000_000) + (microjoules * 1_000_000_000_000) + (millijoules * 1_000_000_000_000_000) + (joules * 1_000_000_000_000_000_000)\n            if (attoJ.isFinite()) return@toInternationalComponents Energy(attoJ / 1_000_000_000_000_000)\n            // ----------- Try femtojoule precision. ------------------------------------------------------\n            val femtoJ = (attojoules / 1_000) + femtojoules + (picojoules * 1_000) + (nanojoules * 1_000_000) + (microjoules * 1_000_000_000) + (millijoules * 1_000_000_000_000) + (joules * 1_000_000_000_000_000)\n            if (femtoJ.isFinite()) return@toInternationalComponents Energy(femtoJ / 1_000_000_000_000)\n            // ----------- Try picojoule precision. ------------------------------------------------------\n            val picoJ = (attojoules / 1_000_000) + (femtojoules / 1_000) + picojoules + (nanojoules * 1_000) + (microjoules * 1_000_000) + (millijoules * 1_000_000_000) + (joules * 1_000_000_000_000)\n            if (picoJ.isFinite()) return@toInternationalComponents Energy(picoJ / 1_000_000_000)\n            // ----------- Try nanojoule precision. ------------------------------------------------------\n            val nanoJ = (attojoules / 1_000_000_000) + (femtojoules / 1_000_000) + (picojoules / 1_000) + nanojoules + (microjoules * 1_000) + (millijoules * 1_000_000) + (joules * 1_000_000_000)\n            if (nanoJ.isFinite()) return@toInternationalComponents Energy(nanoJ / 1_000_000)\n            // ----------- Try microjoule precision. ------------------------------------------------------\n            val microJ = (attojoules / 1_000_000_000_000) + (femtojoules / 1_000_000_000) + (picojoules / 1_000_000) + (nanojoules / 1_000) + microjoules + (millijoules * 1_000) + (joules * 1_000_000)\n            if (microJ.isFinite()) return@toInternationalComponents Energy(microJ / 1_000)\n            // ----------- Default millijoule precision. ------------------------------------------------------\n            val milliJ = (attojoules / 1_000_000_000_000_000) + (femtojoules / 1_000_000_000_000) + (picojoules / 1_000_000_000) + (nanojoules / 1_000_000) + (microjoules / 1_000) + millijoules + (joules * 1_000)\n            Energy(milliJ)\n        }\n    }\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns the number that is the ratio of this and the [other] force value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] force are [infinite][isInfinite].\n     */\n    public operator fun div(other: Force): Double = rawNanonewtons.toDouble() / other.rawNanonewtons.toDouble()\n\n    /**\n     * Returns a force whose value is this force value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Force = div(scale.toLong())\n\n    /**\n     * Returns a force whose value is this force value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * force is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Force = Force(rawNanonewtons / scale)\n\n    /**\n     * Returns the negative of this force value.\n     */\n    public operator fun unaryMinus(): Force = Force(-rawNanonewtons)\n\n    /**\n     * Returns a force whose value is the difference between this and the [other] force value.\n     *\n     * @throws IllegalArgumentException if this force and the [other] force are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Force): Force = Force(rawNanonewtons - other.rawNanonewtons)\n\n    /**\n     * Returns a force whose value is the sum between this and the [other] force value.\n     *\n     * @throws IllegalArgumentException if this force and the [other] force are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Force): Force = Force(rawNanonewtons + other.rawNanonewtons)\n\n    /**\n     * Returns a force whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this force is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Force = times(scale.toLong())\n\n    /**\n     * Returns a force whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this force is [infinite][isInfinite] and [scale] is 0, or when this\n     * force is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Force = Force(rawNanonewtons * scale)\n\n    /**\n     * Returns a force whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this force is [infinite][isInfinite] and [scale] is 0.0 or when this force is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Force = Force(rawNanonewtons * scale)\n\n    /**\n     * Returns a force whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this force is [infinite][isInfinite] and [scale] is 0.0 or when this force is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Force = Force(rawNanonewtons / scale)\n\n    // endregion\n\n    // region Force to Scalar Conversions\n\n    /**\n     * Returns the value of this force expressed as a [Long] number of the specified [unit]. Infinite values are\n     * converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(unit: ForceUnit): Long {\n        return (rawNanonewtons / unit.nanonewtonScale).rawValue\n    }\n\n    /**\n     * Returns the value of this force expressed as a [Double] number of the specified [unit]. Infinite values are\n     * converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] depending on its sign.\n     */\n    public fun toDouble(unit: ForceUnit): Double {\n        return rawNanonewtons.toDouble() / unit.nanonewtonScale\n    }\n\n    /**\n     * Returns a fractional string representation of this force expressed in the specified [ForceUnit] and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(unit: ForceUnit, decimals: Int = 0): String = when {\n        isInfinite() -> rawNanonewtons.toString()\n        else -> buildString {\n            append(toDouble(unit).toDecimalString(decimals))\n            append(unit.symbol)\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this force expressed in the largest [ForceUnit.International]\n     * quantity which is greater than or equal to 1.\n     */\n    public override fun toString(): String {\n        val largestUnit = ForceUnit.International.entries.asReversed().firstOrNull { unit ->\n            rawNanonewtons / unit.nanonewtonScale > 0\n        }\n        return toString(largestUnit ?: ForceUnit.International.Nanonewton, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this area value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawNanonewtons.isInfinite()\n\n    /**\n     * Returns true if this area value is finite.\n     */\n    public fun isFinite(): Boolean = rawNanonewtons.isFinite()\n\n    /**\n     * Compares this force with the [other] force. Returns zero if this force is equal\n     * to the specified [other] force, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    override fun compareTo(other: Force): Int = rawNanonewtons.compareTo(other.rawNanonewtons)\n\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Length.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.POSITIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.isPreciseToNanosecond\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.internal.sign\nimport io.github.kevincianfarini.alchemist.internal.throwIllegalArgumentException\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.scalar.nanometers\nimport io.github.kevincianfarini.alchemist.scalar.nmPerSecond\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Nanometer\nimport kotlin.jvm.JvmInline\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.microseconds\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.nanoseconds\nimport kotlin.time.Duration.Companion.seconds\n\n/**\n * Represents a measure of length and is capable of storing ±9.2 million kilometers at nanometer precision.\n */\n@JvmInline\npublic value class Length internal constructor(internal val rawNanometers: SaturatingLong) : Comparable<Length> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the constant [Velocity] required to travel this length in the specified [duration].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this length or the\n     * other [duration], some precision may be lost.\n     *\n     * @throws IllegalArgumentException if both this length and [duration] are infinite.\n     */\n    public operator fun div(duration: Duration): Velocity = when {\n        rawNanometers.isInfinite() && duration.isInfinite() ->  {\n            throwIllegalArgumentException(\"Dividing two infinite values yields an undefined result.\")\n        }\n        rawNanometers.isInfinite() -> Velocity(rawNanometers * duration.sign)\n        duration.isInfinite() -> Velocity(0L.saturated)\n        else -> calculateVelocity(duration)\n    }\n\n    private fun calculateVelocity(duration: Duration): Velocity {\n        // Try to find the right level which we can perform this operation at without losing precision.\n        if (duration.isPreciseToNanosecond()) {\n            val velocity = nanosPerNs(rawNanometers, duration.inWholeNanoseconds)\n            if (velocity.isFinite()) return velocity\n        }\n        val ms = duration.inWholeMilliseconds\n        val velcity = nanosPerMs(rawNanometers, ms)\n        if (velcity.isFinite()) return velcity\n        return Velocity((rawNanometers / ms) * 1_000)\n    }\n\n    private fun nanosPerMs(nanos: SaturatingLong, ms: Long): Velocity {\n        // 1 nanometer per 1 millisecond is 1,000 nanometers / second.\n        val nanosPerMs = nanos / ms\n        val picoRemainder = (nanos % ms) * 1_000\n        return (nanosPerMs * 1_000).nmPerSecond + picosPerMs(picoRemainder, ms)\n    }\n\n    private fun picosPerMs(picos: SaturatingLong, ms: Long): Velocity {\n        // 1 picometer per 1 millisecond is 1 nanometer / second.\n        return Velocity(picos / ms)\n    }\n\n    private fun nanosPerNs(nanos: SaturatingLong, ns: Long): Velocity {\n        // 1 nanometer per 1 nanosecond is 1,000,000,000 nanometers / second.\n        val nanosPerNs = nanos / ns\n        val picoRemainder = (nanos % ns) * 1_000\n        return (nanosPerNs * 1_000_000_000).nmPerSecond + picosPerNs(picoRemainder, ns)\n    }\n\n    private fun picosPerNs(picos: SaturatingLong, ns: Long): Velocity {\n        // 1 picometer per 1 nanosecond is 1,000,000 nanometers / second.\n        val picosPerNs = picos / ns\n        val femtoRemainder = (picos % ns) * 1_000\n        return (picosPerNs * 1_000_000).nmPerSecond + femtosPerNs(femtoRemainder, ns)\n    }\n\n    private fun femtosPerNs(femtos: SaturatingLong, ns: Long): Velocity {\n        // 1 femtometer per 1 nanosecond is 1,000 nanometers / second.\n        val femtosPerNs = femtos / ns\n        val attoRemainder = (femtos % ns) * 1_000\n        return (femtosPerNs * 1_000).nmPerSecond + attometersPerNs(attoRemainder, ns)\n    }\n\n    private fun attometersPerNs(attos: SaturatingLong, ns: Long): Velocity {\n        // 1 attometer per 1 nanosecond is 1 nanometer / second.\n        return Velocity(attos / ns)\n    }\n\n    /**\n     * Returns the [Duration] required to travel this length at the specified constant [velocity].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this length or the\n     * other [velocity], some precision may be lost.\n     *\n     * @throws IllegalArgumentException if both this length and [velocity] are infinite.\n     */\n    public operator fun div(velocity: Velocity): Duration = when {\n        isInfinite() && velocity.isInfinite() -> {\n            throwIllegalArgumentException(\"Dividing two infinite values yields an undefined result.\")\n        }\n        isInfinite() -> Duration.INFINITE / velocity.rawNanometersPerSecond.sign / rawNanometers.sign\n        velocity.isInfinite() -> Duration.ZERO\n        else -> calculateDuration(velocity)\n    }\n\n    private fun calculateDuration(velocity: Velocity): Duration {\n        val duration = seconds(rawNanometers, velocity.rawNanometersPerSecond)\n        return if (duration.isFinite()) {\n            duration\n        } else {\n            // Do coarse operation to avoid returning infinity.\n            (rawNanometers / velocity.rawNanometersPerSecond).rawValue.seconds\n        }\n    }\n\n    private fun seconds(nanometers: SaturatingLong, nanometersPerSecond: SaturatingLong): Duration {\n        // 1 nanometer divided by 1 nm/s is 1 second.\n        val seconds = (nanometers / nanometersPerSecond).rawValue.seconds\n        val picometers = (nanometers % nanometersPerSecond) * 1_000\n        return seconds + milliseconds(picometers, nanometersPerSecond)\n    }\n\n    private fun milliseconds(picometers: SaturatingLong, nanometersPerSecond: SaturatingLong): Duration {\n        // 1 picometer divided by 1 nm/s is 1 millisecond.\n        val milliseconds = (picometers / nanometersPerSecond).rawValue.milliseconds\n        val femtometers = (picometers % nanometersPerSecond) * 1_000\n        return milliseconds + microseconds(femtometers, nanometersPerSecond)\n    }\n\n    private fun microseconds(femtometers: SaturatingLong, nanometersPerSecond: SaturatingLong): Duration {\n        // 1 femtometer divided by 1 nm/s is 1 microsecond.\n        val microseconds = (femtometers / nanometersPerSecond).rawValue.microseconds\n        val attometers = (femtometers % nanometersPerSecond) * 1_000\n        return microseconds + nanoseconds(attometers, nanometersPerSecond)\n    }\n\n    private fun nanoseconds(attometers: SaturatingLong, nanometersPerSecond: SaturatingLong): Duration {\n        // 1 attometer divided by 1 nm/s is 1 nanosecond.\n        return (attometers / nanometersPerSecond).rawValue.nanoseconds\n    }\n\n    /**\n     * Returns the resulting [Area] after multiplying this length by the [other] length value.\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this length or the\n     * [other] length, some precision may be lost.\n     *\n     * @throws IllegalArgumentException when this length is [infinite][isInfinite] and [other] is 0 or vice versa.\n     */\n    public operator fun times(other: Length): Area {\n        // Omit micrometer and nanometer components for now. The maximum value these components could ever produce is\n        // 998,001,998,001 nanometers², and therefore micrometers and nanometers are always lost to precision rounding\n        // when converting to millimeters². In the future we may choose more precise measures of Area and this might\n        // be revisited.\n        return toSaturatedInternationalComponents { giga, mega, kilo, meters, centi, milli, _, _ ->\n            other.toSaturatedInternationalComponents { otherGiga, otherMega, otherKilo, otherMeters, otherCenti, otherMilli, _, _ ->\n                val gigaSquared = giga * otherGiga\n                val megaSquared = mega * otherMega\n                val kiloSquared = kilo * otherKilo\n                val metersSquared = meters * otherMeters\n                val centiSquared = centi * otherCenti\n                val millisSquared = milli * otherMilli\n                if (gigaSquared != 0L.saturated) {\n                    // We can't represent gigameter² at millimeter² precision.\n                    Area(POSITIVE_INFINITY * gigaSquared.sign)\n                } else {\n                    val megaMillis = megaSquared * 1_000_000_000_000_000_000\n                    val kiloMillis = kiloSquared * 1_000_000_000_000\n                    val meterMillis = metersSquared * 1_000_000\n                    val centiMillis = centiSquared * 100\n                    Area(megaMillis + kiloMillis + meterMillis + centiMillis + millisSquared)\n                }\n            }\n        }\n    }\n\n    /**\n     * Returns the resulting [Volume] after applying this length over the specified [area].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of this length or the\n     * specified [area] some precision may be lost.\n     *\n     * @throws IllegalArgumentException if this length is [infinite][isInfinite] and [area] is zero, or if this length\n     * is zero and [area] is infinite.\n     */\n    public operator fun times(area: Area): Volume = area * this\n\n    /**\n     * Returns the amount of [Energy] required to apply the specified [force] over this length.\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this length or the\n     * specified [force], some precision may be lost.\n     *\n     * @throws IllegalArgumentException when this length is [infinite][isInfinite] and [force] is 0 or vice versa.\n     */\n    public operator fun times(force: Force): Energy = force * this\n\n    /**\n     * Returns an [Area] representing a square with two dimensions of this length.\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this length some\n     * precision may be lost.\n     */\n    public fun squared(): Area = this * this\n\n    /**\n     * Returns a [Volume] representing a cube with three dimensions of this length.\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this length some\n     * precision may be lost.\n     */\n    public fun cubed(): Volume = this * this * this\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns the number that is the ratio of this and the [other] length value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] length are [infinite][isInfinite].\n     */\n    public operator fun div(other: Length): Double {\n        return rawNanometers.toDouble() / other.rawNanometers.toDouble()\n    }\n\n    /**\n     * Returns a length whose value is this length value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Length {\n        return div(scale.toLong())\n    }\n\n    /**\n     * Returns a length whose value is this length value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * length is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Length {\n        return Length(rawNanometers / scale)\n    }\n\n    /**\n     * Returns the negative of this length value.\n     */\n    public operator fun unaryMinus(): Length = Length(-rawNanometers)\n\n    /**\n     * Returns a length whose value is the difference between this and the [other] length value.\n     *\n     * @throws IllegalArgumentException if this length and the [other] length are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Length): Length {\n        return Length(rawNanometers - other.rawNanometers)\n    }\n\n    /**\n     * Returns a length whose value is the sum between this and the [other] length value.\n     *\n     * @throws IllegalArgumentException if this length and the [other] length are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Length): Length {\n        return Length(rawNanometers + other.rawNanometers)\n    }\n\n    /**\n     * Returns a length whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this length is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Length {\n        return times(scale.toLong())\n    }\n\n    /**\n     * Returns a length whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this length is [infinite][isInfinite] and [scale] is 0, or when this\n     * length is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Length {\n        return Length(rawNanometers * scale)\n    }\n\n    /**\n     * Returns a length whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this length is [infinite][isInfinite] and [scale] is 0.0 or when this length is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Length = Length(rawNanometers * scale)\n\n    /**\n     * Returns a length whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this length is [infinite][isInfinite] and [scale] is 0.0 or when this length is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Length = Length(rawNanometers / scale)\n\n    // endregion\n\n    // region Length to Scalar Conversions\n\n    private fun <T> toSaturatedInternationalComponents(\n        action: (\n            gigameters: SaturatingLong,\n            megameters: SaturatingLong,\n            kilometers: SaturatingLong,\n            meters: SaturatingLong,\n            centimeters: SaturatingLong,\n            millimeters: SaturatingLong,\n            micrometers: SaturatingLong,\n            nanometers: SaturatingLong,\n        ) -> T,\n    ): T {\n        val giga = rawNanometers / LengthUnit.International.Gigameter.nanometerScale\n        val gigaRemainder = rawNanometers % LengthUnit.International.Gigameter.nanometerScale\n        val mega = gigaRemainder / LengthUnit.International.Megameter.nanometerScale\n        val megaRemainder = gigaRemainder % LengthUnit.International.Megameter.nanometerScale\n        val kilo = megaRemainder / LengthUnit.International.Kilometer.nanometerScale\n        val kiloRemainder = megaRemainder % LengthUnit.International.Kilometer.nanometerScale\n        val meters = kiloRemainder / LengthUnit.International.Meter.nanometerScale\n        val metersRemainder = kiloRemainder % LengthUnit.International.Meter.nanometerScale\n        val centi = metersRemainder / LengthUnit.International.Centimeter.nanometerScale\n        val centiRemainder = metersRemainder % LengthUnit.International.Centimeter.nanometerScale\n        val milli = centiRemainder / LengthUnit.International.Millimeter.nanometerScale\n        val milliRemainder = centiRemainder % LengthUnit.International.Millimeter.nanometerScale\n        val micro = milliRemainder / LengthUnit.International.Micrometer.nanometerScale\n        val nano = milliRemainder % LengthUnit.International.Micrometer.nanometerScale\n        return action(giga, mega, kilo, meters, centi, milli, micro, nano)\n    }\n\n    /**\n     * Splits this length into gigameters, megameters, kilometers, meters, centimeters, millimeters, micrometers, and\n     * nanometers and executes the [action] with those components. The result of [action] is returned as the result of\n     * this function.\n     *\n     * Infinite length values invoke [action] with [Long.MAX_VALUE] or [Long.MIN_VALUE] for every component, depending\n     * on the infinite value's sign.\n     */\n    public fun <T> toInternationalComponents(\n        action: (\n            gigameters: Long,\n            megameters: Long,\n            kilometers: Long,\n            meters: Long,\n            centimeters: Long,\n            millimeters: Long,\n            micrometers: Long,\n            nanometers: Long,\n        ) -> T,\n    ): T = toSaturatedInternationalComponents { giga, mega, kilo, meters, centi, milli, micro, nano ->\n        action(\n            giga.rawValue,\n            mega.rawValue,\n            kilo.rawValue,\n            meters.rawValue,\n            centi.rawValue,\n            milli.rawValue,\n            micro.rawValue,\n            nano.rawValue,\n        )\n    }\n\n    /**\n     * Splits this length into miles, yards, feet, and inches and executes the [action] with those components. The\n     * result of [action] is returned as the result of this function.\n     *\n     * Infinite length values invoke [action] with [Long.MAX_VALUE], [Long.MIN_VALUE], [Double.POSITIVE_INFINITY], or\n     * [Double.NEGATIVE_INFINITY] for every component, depending on the infinite value's sign and the component's type.\n     */\n    public fun <T> toUnitedStatesCustomaryComponents(\n        action: (miles: Long, yards: Long, feet: Long, inches: Double) -> T,\n    ): T {\n        val miles = rawNanometers / LengthUnit.UnitedStatesCustomary.Mile.nanometerScale\n        val milesRemainder = rawNanometers % LengthUnit.UnitedStatesCustomary.Mile.nanometerScale\n        val yards = milesRemainder / LengthUnit.UnitedStatesCustomary.Yard.nanometerScale\n        val yardRemainder = milesRemainder % LengthUnit.UnitedStatesCustomary.Yard.nanometerScale\n        val feet = yardRemainder / LengthUnit.UnitedStatesCustomary.Foot.nanometerScale\n        val feetRemainder = yardRemainder % LengthUnit.UnitedStatesCustomary.Foot.nanometerScale\n        val inches = feetRemainder.rawValue.nanometers.toDouble(LengthUnit.UnitedStatesCustomary.Inch)\n        return action(miles.rawValue, yards.rawValue, feet.rawValue, inches)\n    }\n\n    /**\n     * Returns the value of this length expressed as a [Long] number of the specified [unit]. Infinite values are\n     * converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(unit: LengthUnit): Long {\n        return (rawNanometers / unit.nanometerScale).rawValue\n    }\n\n    /**\n     * Returns the value of this length expressed as a [Double] number of the specified [unit]. Infinite values are\n     * converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] depending on its sign.\n     */\n    public fun toDouble(unit: LengthUnit): Double {\n        return this / unit.nanometerScale.nanometers\n    }\n\n    /**\n     * Returns a fractional string representation of this length expressed in the specified [unit] and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(unit: LengthUnit, decimals: Int = 0): String = when (rawNanometers.isInfinite()) {\n        true -> rawNanometers.toString()\n        false -> buildString {\n            append(toDouble(unit).toDecimalString(decimals))\n            append(unit.symbol)\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this length expressed in the largest [LengthUnit.International]\n     * quantity which is greater than or equal to 1.\n     */\n    public override fun toString(): String {\n        val largestUnit = LengthUnit.International.entries.asReversed().firstOrNull { unit ->\n            rawNanometers.absoluteValue / unit.nanometerScale > 0\n        }\n        return toString(largestUnit ?: Nanometer, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this length value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawNanometers.isInfinite()\n\n    /**\n     * Returns true if this length value is finite.\n     */\n    public fun isFinite(): Boolean = rawNanometers.isFinite()\n\n    /**\n     * Compares this length with the [other] length. Returns zero if this length is equal\n     * to the specified [other] length, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    public override fun compareTo(other: Length): Int {\n        return rawNanometers.compareTo(other.rawNanometers)\n    }\n\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Mass.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.unit.MassUnit\nimport kotlin.jvm.JvmInline\nimport kotlin.text.Typography.nbsp\n\n/**\n * Represents a measure of mass and is capable of storing ±9.2 billion kilograms at microgram precision.\n */\n@JvmInline\npublic value class Mass internal constructor(internal val rawMicrograms: SaturatingLong) : Comparable<Mass> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the [Force] required to apply to this mass to achieve the specified [acceleration].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this mass or the\n     * specified [acceleration], some precision may be lost.\n     *\n     * @throws IllegalArgumentException when [acceleration] is [infinite][isInfinite] and this mass is 0 or vice versa.\n     */\n    public operator fun times(acceleration: Acceleration): Force = acceleration * this\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns a mass whose value is this mass value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Mass = div(scale.toLong())\n\n    /**\n     * Returns a mass whose value is this mass value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * mass is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Mass = Mass(rawMicrograms / scale)\n\n    /**\n     * Returns the number that is the ratio of this and the [other] mass value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] mass are [infinite][isInfinite].\n     */\n    public operator fun div(other: Mass): Double = rawMicrograms.toDouble() / other.rawMicrograms.toDouble()\n\n    /**\n     * Returns the negative of this mass value.\n     */\n    public operator fun unaryMinus(): Mass = Mass(-rawMicrograms)\n\n    /**\n     * Returns a mass whose value is the difference between this and the [other] mass value.\n     *\n     * @throws IllegalArgumentException if this mass and the [other] mass are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Mass): Mass = Mass(rawMicrograms - other.rawMicrograms)\n\n    /**\n     * Returns a mass whose value is the sum between this and the [other] mass value.\n     *\n     * @throws IllegalArgumentException if this mass and the [other] mass are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Mass): Mass = Mass(rawMicrograms + other.rawMicrograms)\n\n    /**\n     * Returns a mass whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this mass is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Mass = times(scale.toLong())\n\n    /**\n     * Returns a mass whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this mass is [infinite][isInfinite] and [scale] is 0, or when this\n     * mass is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Mass = Mass(rawMicrograms * scale)\n\n    /**\n     * Returns a mass whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this mass is [infinite][isInfinite] and [scale] is 0.0 or when this mass is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Mass = Mass(rawMicrograms * scale)\n\n    /**\n     * Returns a mass whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this mass is [infinite][isInfinite] and [scale] is 0.0 or when this mass is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Mass = Mass(rawMicrograms / scale)\n\n    // endregion\n\n    // region Mass to Scalar Conversions\n\n    /**\n     * Splits this mass into teragrams, gigagrams, megagrams, kilograms, grams, milligrams, and micrograms and\n     * executes the [action] with those components. The result of [action] is returned as the result of this function.\n     *\n     * Infinite mass values invoke [action] with [Long.MAX_VALUE] or [Long.MIN_VALUE] for every component, depending\n     * on the infinite value's sign.\n     */\n    public fun <T> toInternationalComponents(\n        action: (\n            teragrams: Long,\n            gigagrams: Long,\n            megagrams: Long,\n            kilograms: Long,\n            grams: Long,\n            milligrams: Long,\n            micrograms: Long,\n        ) -> T\n    ): T {\n        val tera = rawMicrograms / MassUnit.International.Teragram.microgramScale\n        val teraRemainder = rawMicrograms % MassUnit.International.Teragram.microgramScale\n        val giga = teraRemainder / MassUnit.International.Gigagram.microgramScale\n        val gigaRemainder = teraRemainder % MassUnit.International.Gigagram.microgramScale\n        val mega = gigaRemainder / MassUnit.International.Megagram.microgramScale\n        val megaRemainder = gigaRemainder % MassUnit.International.Megagram.microgramScale\n        val kilo = megaRemainder / MassUnit.International.Kilogram.microgramScale\n        val kiloRemainder = megaRemainder % MassUnit.International.Kilogram.microgramScale\n        val grams = kiloRemainder / MassUnit.International.Gram.microgramScale\n        val gramRemainder = kiloRemainder % MassUnit.International.Gram.microgramScale\n        val milli = gramRemainder / MassUnit.International.Milligram.microgramScale\n        val micro = gramRemainder % MassUnit.International.Megagram.microgramScale\n        return action(\n            tera.rawValue,\n            giga.rawValue,\n            mega.rawValue,\n            kilo.rawValue,\n            grams.rawValue,\n            milli.rawValue,\n            micro.rawValue\n        )\n    }\n\n    /**\n     * Returns the value of this mass expressed as a [Long] number of the specified [unit]. Infinite values are\n     * converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(unit: MassUnit): Long {\n        return (rawMicrograms / unit.microgramScale).rawValue\n    }\n\n    /**\n     * Returns the value of this mass expressed as a [Double] number of the specified [unit]. Infinite values are\n     * converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] depending on its sign.\n     */\n    public fun toDouble(unit: MassUnit): Double {\n        return rawMicrograms.toDouble() / unit.microgramScale.toDouble()\n    }\n\n    /**\n     * Returns a fractional string representation of this mass expressed in the specified [unit] and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(unit: MassUnit, decimals: Int = 0): String = when (isInfinite()) {\n        true -> rawMicrograms.toString()\n        false -> buildString {\n            append(toDouble(unit).toDecimalString(decimals))\n            append(nbsp)\n            append(unit.symbol)\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this mass expressed in the largest [MassUnit.International]\n     * quantity which is greater than or equal to 1.\n     */\n    override fun toString(): String {\n        val largestUnit = MassUnit.International.entries.asReversed().firstOrNull { unit ->\n            rawMicrograms.absoluteValue / unit.microgramScale > 0\n        }\n        return toString(largestUnit ?: MassUnit.International.Microgram, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this area value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawMicrograms.isInfinite()\n\n    /**\n     * Returns true if this area value is finite.\n     */\n    public fun isFinite(): Boolean = rawMicrograms.isFinite()\n\n    /**\n     * Compares this mass with the [other] mass. Returns zero if this mass is equal\n     * to the specified [other] mass, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    override fun compareTo(other: Mass): Int = rawMicrograms.compareTo(other.rawMicrograms)\n\n    // endregion\n}\n\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Power.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.POSITIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.sign\nimport io.github.kevincianfarini.alchemist.internal.toDecimalComponents\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.scalar.microwatts\nimport io.github.kevincianfarini.alchemist.unit.PowerUnit\nimport kotlin.jvm.JvmInline\nimport kotlin.time.Duration\n\n/**\n * Represents an amount of power and is capable of storing ±9.22 terawatts at microwatt precision.\n */\n@JvmInline\npublic value class Power internal constructor(private val rawMicrowatts: SaturatingLong) : Comparable<Power> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the resulting [Energy] from applying this power over the specified [duration].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this power or [duration],\n     * some precision may be lost.\n     *\n     * @throws IllegalArgumentException if this power is infinite and duration is zero, or if this power is zero and\n     * duration is infinite.\n     */\n    public operator fun times(duration: Duration): Energy {\n        return when {\n            duration.isInfinite() || rawMicrowatts.isInfinite() -> {\n                Energy(POSITIVE_INFINITY * duration.sign * rawMicrowatts)\n            }\n            else -> duration.toDecimalComponents { kiloseconds, seconds, millis, micros, nanos ->\n                // Try to find the right level which we can perform this operation at without losing precision.\n                // --------------------------------------------------------------------------------------------\n                // 1 microwatt * 1 nanosecond is 1 femtojoule.\n                // 1 microwatt * 1 microsecond is 1 picojoule.\n                // 1 microwatt * 1 millisecond is 1 nanojoule.\n                // 1 microwatt * 1 second is 1 microjoule.\n                // 1 microwatt * 1,000 seconds is 1 millijoule.\n                // --------------------------------------------------------------------------------------------\n                val millijoules = rawMicrowatts * kiloseconds\n                val microjoules = rawMicrowatts * seconds\n                val nanojoules = rawMicrowatts * millis\n                val picojoules = rawMicrowatts * micros\n                val femtojoules = rawMicrowatts * nanos\n                // ----------- Try femtojoule precision. ------------------------------------------------------\n                val femtoJ = femtojoules + (picojoules * 1_000) + (nanojoules * 1_000_000) + (microjoules * 1_000_000_000) + (millijoules * 1_000_000_000_000)\n                if (femtoJ.isFinite()) return@toDecimalComponents Energy(femtoJ / 1_000_000_000_000)\n                // ----------- Try picojoule precision. -------------------------------------------------------\n                val picoJ = (femtojoules / 1_000) + picojoules + (nanojoules * 1_000) + (microjoules * 1_000_000) + (millijoules * 1_000_000_000)\n                if (picoJ.isFinite()) return@toDecimalComponents Energy(picoJ / 1_000_000_000)\n                // ----------- Try nanojoule precision. -------------------------------------------------------\n                val nanoJ = (femtojoules / 1_000_000) + (picojoules / 1_000) + nanojoules + (microjoules * 1_000) + (millijoules * 1_000_000)\n                if (nanoJ.isFinite()) return@toDecimalComponents Energy(nanoJ / 1_000_000)\n                // ----------- Try microjoule precision. -------------------------------------------------------\n                val microJ = (femtojoules / 1_000_000_000) + (picojoules / 1_000_000) + (nanojoules / 1_000) + microjoules + (millijoules * 1_000)\n                if (microJ.isFinite()) return@toDecimalComponents Energy(microJ / 1_000)\n                // ----------- Default microjoule precision. ---------------------------------------------------\n                val milliJ = (femtojoules / 1_000_000_000_000) + (picojoules / 1_000_000_000) + (nanojoules / 1_000_000) + (microjoules / 1_000) + millijoules\n                Energy(milliJ)\n            }\n        }\n    }\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns the number that is the ratio of this and the [other] power value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] power are [infinite][isInfinite].\n     */\n    public operator fun div(other: Power): Double {\n        return rawMicrowatts.toDouble() / other.rawMicrowatts.toDouble()\n    }\n\n    /**\n     * Returns a power whose value is this power value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Power = div(scale.toLong())\n\n    /**\n     * Returns a power whose value is this power value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * power is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Power = Power(rawMicrowatts / scale)\n\n    /**\n     * Returns the negative of this power value.\n     */\n    public operator fun unaryMinus(): Power = Power(-rawMicrowatts)\n\n    /**\n     * Returns a power whose value is the difference between this and the [other] power value.\n     *\n     * @throws IllegalArgumentException if this power and the [other] power are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Power): Power = Power(rawMicrowatts - other.rawMicrowatts)\n\n    /**\n     * Returns a power whose value is the sum between this and the [other] power value.\n     *\n     * @throws IllegalArgumentException if this power and the [other] power are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Power): Power = Power(rawMicrowatts + other.rawMicrowatts)\n\n    /**\n     * Returns a power whose value is this power multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this power is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Power = times(scale.toLong())\n\n    /**\n     * Returns a power whose value is this power multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this power is [infinite][isInfinite] and [scale] is 0, or when this\n     * power is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Power = Power(rawMicrowatts * scale)\n\n    /**\n     * Returns a power whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this power is [infinite][isInfinite] and [scale] is 0.0 or when this power is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Power = Power(rawMicrowatts * scale)\n\n    /**\n     * Returns a power whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this power is [infinite][isInfinite] and [scale] is 0.0 or when this power is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Power = Power(rawMicrowatts / scale)\n\n    // endregion\n\n    // region Power to Scalar Conversions\n\n    /**\n     * Returns the value of this power expressed as a [Long] number of the specified [unit]. Infinite values are\n     * converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(unit: PowerUnit): Long {\n        return (rawMicrowatts / unit.microwattScale).rawValue\n    }\n\n    /**\n     * Returns the value of this power expressed as a [Double] number of the specified [unit]. Infinite values are\n     * converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] depending on its sign.\n     */\n    public fun toDouble(unit: PowerUnit): Double {\n        return this / unit.microwattScale.microwatts\n    }\n\n    /**\n     * Returns a fractional string representation of this power expressed in the specified [unit] and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(unit: PowerUnit, decimals: Int = 0): String = when (rawMicrowatts.isInfinite()) {\n        true -> rawMicrowatts.toString()\n        false -> buildString {\n            append(toDouble(unit).toDecimalString(decimals))\n            append(unit.symbol)\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this power expressed in the largest [PowerUnit.International]\n     * quantity which is greater than or equal to 1.\n     */\n    override fun toString(): String {\n        val largestUnit = PowerUnit.International.entries.asReversed().firstOrNull { unit ->\n            rawMicrowatts.absoluteValue / unit.microwattScale > 0\n        }\n        return toString(largestUnit ?: PowerUnit.International.Microwatt, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this area value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawMicrowatts.isInfinite()\n\n    /**\n     * Returns true if this area value is finite.\n     */\n    public fun isFinite(): Boolean = rawMicrowatts.isFinite()\n\n    /**\n     * Compares this power with the [other] power. Returns zero if this power is equal\n     * to the specified [other] power, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    public override fun compareTo(other: Power): Int {\n        return rawMicrowatts.compareTo(other.rawMicrowatts)\n    }\n\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Temperature.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.unit.TemperatureUnit\nimport io.github.kevincianfarini.alchemist.unit.convertNanokelvinsToThis\nimport kotlin.jvm.JvmInline\nimport kotlin.math.roundToLong\n\n/**\n * Represents a temperature and is capable of storing ±9.2 billion Kelvin (±9.2 billion °C, ±16.6 billion °F) at\n * nanokelvin precision.\n */\n@JvmInline\npublic value class Temperature internal constructor(private val rawNanokelvin: SaturatingLong) : Comparable<Temperature> {\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns the number that is the ratio of this and the [other] temperature value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] temperature are [infinite][isInfinite].\n     */\n    public operator fun div(other: Temperature): Double {\n        return rawNanokelvin.toDouble() / other.rawNanokelvin.toDouble()\n    }\n\n    /**\n     * Returns a temperature whose value is this temperature value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Temperature = div(scale.toLong())\n\n    /**\n     * Returns a temperature whose value is this temperature value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * temperature is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Temperature = Temperature(rawNanokelvin / scale)\n\n    /**\n     * Returns the negative of this temperature value.\n     */\n    public operator fun unaryMinus(): Temperature = Temperature(-rawNanokelvin)\n\n    /**\n     * Returns a temperature whose value is the difference between this and the [other] temperature value.\n     *\n     * @throws IllegalArgumentException if this temperature and the [other] temperature are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Temperature): Temperature = Temperature(rawNanokelvin - other.rawNanokelvin)\n\n    /**\n     * Returns a temperature whose value is the sum between this and the [other] temperature value.\n     *\n     * @throws IllegalArgumentException if this temperature and the [other] temperature are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Temperature): Temperature = Temperature(rawNanokelvin + other.rawNanokelvin)\n\n    /**\n     * Returns a temperature whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this temperature is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Temperature = times(scale.toLong())\n\n    /**\n     * Returns a temperature whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this temperature is [infinite][isInfinite] and [scale] is 0, or when this\n     * temperature is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Temperature = Temperature(rawNanokelvin * scale)\n\n    // endregion\n\n    // region Temperature to Scalar Conversions\n\n    /**\n     * Returns the value of this temperature expressed as a [Long] number of the specified [unit]. Infinite values are\n     * converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(unit: TemperatureUnit): Long = toDouble(unit).roundToLong()\n\n    /**\n     * Returns the value of this temperature expressed as a [Double] number of the specified [unit]. Infinite values are\n     * converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] depending on its sign.\n     */\n    public fun toDouble(unit: TemperatureUnit): Double = unit.convertNanokelvinsToThis(rawNanokelvin)\n\n    /**\n     * Returns a fractional string representation of this temperature expressed in the specified [unit] and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(unit: TemperatureUnit, decimals: Int = 0): String {\n        return buildString {\n            append(unit.convertNanokelvinsToThis(rawNanokelvin).toDecimalString(decimals))\n            append(unit.symbol)\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this temperature expressed in the largest\n     * [TemperatureUnit.International] quantity which is greater than or equal to 1.\n     */\n    public override fun toString(): String {\n        return toString(toStringUnit(), decimals = 2)\n    }\n\n    private fun toStringUnit(): TemperatureUnit = when {\n        TemperatureUnit.International.Gigakelvin.convertNanokelvinsToThis(rawNanokelvin) >= 1.0 -> {\n            TemperatureUnit.International.Gigakelvin\n        }\n        TemperatureUnit.International.Megakelvin.convertNanokelvinsToThis(rawNanokelvin) >= 1.0 -> {\n            TemperatureUnit.International.Megakelvin\n        }\n        TemperatureUnit.International.Kilokelvin.convertNanokelvinsToThis(rawNanokelvin) >= 1.0 -> {\n            TemperatureUnit.International.Kilokelvin\n        }\n        TemperatureUnit.International.Kelvin.convertNanokelvinsToThis(rawNanokelvin) >= 1.0 -> {\n            TemperatureUnit.International.Kelvin\n        }\n        TemperatureUnit.International.Millikelvin.convertNanokelvinsToThis(rawNanokelvin) >= 1.0 -> {\n            TemperatureUnit.International.Millikelvin\n        }\n        TemperatureUnit.International.Microkelvin.convertNanokelvinsToThis(rawNanokelvin) >= 1.0 -> {\n            TemperatureUnit.International.Microkelvin\n        }\n        else -> TemperatureUnit.International.Nanokelvin\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this area value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawNanokelvin.isInfinite()\n\n    /**\n     * Returns true if this area value is finite.\n     */\n    public fun isFinite(): Boolean = rawNanokelvin.isFinite()\n\n    /**\n     * Compares this temperature with the [other] temperature. Returns zero if this temperature is equal\n     * to the specified [other] temperature, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    public override fun compareTo(other: Temperature): Int {\n        return rawNanokelvin.compareTo(other.rawNanokelvin)\n    }\n\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Velocity.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.isPreciseToNanosecond\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport io.github.kevincianfarini.alchemist.internal.secondScale\nimport io.github.kevincianfarini.alchemist.internal.shortName\nimport io.github.kevincianfarini.alchemist.internal.sign\nimport io.github.kevincianfarini.alchemist.internal.throwIllegalArgumentException\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.scalar.nanometers\nimport io.github.kevincianfarini.alchemist.scalar.nmPerSecond2\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport kotlin.jvm.JvmInline\nimport kotlin.math.roundToLong\nimport kotlin.text.Typography.nbsp\nimport kotlin.time.Duration\nimport kotlin.time.DurationUnit\n\n/**\n * Represents a measure of velocity and is capable of storing ±9.2 million km/s at nm/s precision.\n */\n@JvmInline\npublic value class Velocity internal constructor(\n    internal val rawNanometersPerSecond: SaturatingLong\n) : Comparable<Velocity> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the constant [Acceleration] required achieve this velocity in the specified [duration].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this velocity or the\n     * other [duration], some precision may be lost.\n     *\n     * @throws IllegalArgumentException if both this velocity and [duration] are infinite.\n     */\n    public operator fun div(duration: Duration): Acceleration = when {\n        isInfinite() && duration.isInfinite() -> {\n            throwIllegalArgumentException(\"Dividing two infinite values yields an undefined result.\")\n        }\n        isInfinite() -> Acceleration(rawNanometersPerSecond * duration.sign)\n        duration.isInfinite() -> Acceleration(0L.saturated)\n        else -> calculateAcceleration(duration)\n    }\n\n    private fun calculateAcceleration(duration: Duration): Acceleration {\n        // Try to find the right level which we can perform this operation at without losing precision.\n        if (duration.isPreciseToNanosecond()) {\n            val acceleration = nanosPerNs2(rawNanometersPerSecond, duration.inWholeNanoseconds)\n            if (acceleration.isFinite()) return acceleration\n        }\n        val ms = duration.inWholeMilliseconds\n        val acceleration = nanosPerMs2(rawNanometersPerSecond, ms)\n        if (acceleration.isFinite()) return acceleration\n        return Acceleration((rawNanometersPerSecond / ms) * 1_000)\n    }\n\n    private fun nanosPerNs2(nanosPerSecond: SaturatingLong, ns: Long): Acceleration {\n        val nanos = nanosPerSecond / ns\n        val picoRemainder = (nanosPerSecond % ns) * 1_000\n        return (nanos * 1_000_000_000).nmPerSecond2 + picosPerNs2(picoRemainder, ns)\n    }\n\n    private fun picosPerNs2(picosPerSecond: SaturatingLong, ns: Long): Acceleration {\n        val picos = picosPerSecond / ns\n        val femtoRemainder = (picosPerSecond % ns) * 1_000\n        return (picos * 1_000_000).nmPerSecond2 + femtosPerNs2(femtoRemainder, ns)\n    }\n\n    private fun femtosPerNs2(femtosPerSecond: SaturatingLong, ns: Long): Acceleration {\n        val femtos = femtosPerSecond / ns\n        val attoRemainder = (femtosPerSecond % ns) * 1_000\n        return (femtos * 1_000).nmPerSecond2 + attosPerNs2(attoRemainder, ns)\n    }\n\n    private fun attosPerNs2(attosPerSecond: SaturatingLong, ns: Long): Acceleration {\n        return Acceleration(attosPerSecond / ns)\n    }\n\n    private fun nanosPerMs2(nanosPerSecond: SaturatingLong, ms: Long): Acceleration {\n        val nanos = nanosPerSecond / ms\n        val picoRemainder = (nanosPerSecond % ms) * 1_000\n        return (nanos * 1_000).nmPerSecond2 + picosPerMs2(picoRemainder, ms)\n    }\n\n    private fun picosPerMs2(picosPerSecond: SaturatingLong, ms: Long): Acceleration {\n        return Acceleration(picosPerSecond / ms)\n    }\n\n    /**\n     * Returns the [Length] traveled at this velocity for the specified [duration].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this velocity or\n     * [duration], some precision may be lost.\n     *\n     * @throws IllegalArgumentException when this velocity is [infinite][isInfinite] and [duration] is 0 or vice versa.\n     */\n    public operator fun times(duration: Duration): Length = duration.toComponents { seconds, nanoseconds ->\n        val secondComponent = (rawNanometersPerSecond * seconds).nanometers\n        val preciseNanosecondComponent = ((rawNanometersPerSecond * nanoseconds) / 1_000_000_000).nanometers\n        if (preciseNanosecondComponent.isFinite()) {\n            secondComponent + preciseNanosecondComponent\n        } else {\n            val coarseNanosecondComponent = ((rawNanometersPerSecond / 1_000_000_000) * nanoseconds).nanometers\n            secondComponent + coarseNanosecondComponent\n        }\n    }\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns the number that is the ratio of this and the [other] velocity value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] velocity are [infinite][isInfinite].\n     */\n    public operator fun div(other: Velocity): Double {\n        return rawNanometersPerSecond.toDouble() / other.rawNanometersPerSecond.toDouble()\n    }\n\n    /**\n     * Returns a velocity whose value is this velocity value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Velocity = div(scale.toLong())\n\n    /**\n     * Returns a velocity whose value is this velocity value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * velocity is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Velocity = Velocity(rawNanometersPerSecond / scale)\n\n    /**\n     * Returns the negative of this velocity value.\n     */\n    public operator fun unaryMinus(): Velocity = Velocity(-rawNanometersPerSecond)\n\n    /**\n     * Returns a velocity whose value is the difference between this and the [other] velocity value.\n     *\n     * @throws IllegalArgumentException if this velocity and the [other] velocity are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Velocity): Velocity {\n        return Velocity(rawNanometersPerSecond - other.rawNanometersPerSecond)\n    }\n\n    /**\n     * Returns a velocity whose value is the sum between this and the [other] velocity value.\n     *\n     * @throws IllegalArgumentException if this velocity and the [other] velocity are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Velocity): Velocity {\n        return Velocity(rawNanometersPerSecond + other.rawNanometersPerSecond)\n    }\n\n    /**\n     * Returns a velocity whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this velocity is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Velocity = times(scale.toLong())\n\n    /**\n     * Returns a velocity whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this velocity is [infinite][isInfinite] and [scale] is 0, or when this\n     * velocity is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Velocity = Velocity(rawNanometersPerSecond * scale)\n\n    /**\n     * Returns a velocity whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this velocity is [infinite][isInfinite] and [scale] is 0.0 or when this velocity is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Velocity = Velocity(rawNanometersPerSecond * scale)\n\n    /**\n     * Returns a velocity whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this velocity is [infinite][isInfinite] and [scale] is 0.0 or when this velocity is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Velocity = Velocity(rawNanometersPerSecond / scale)\n\n    // endregion\n\n    // region Velocity to Scalar Conversions\n\n    /**\n     * Returns the value of this velocity expressed as a [Long] number of the specified [lengthUnit] per [durationUnit].\n     * Infinite values are converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(lengthUnit: LengthUnit, durationUnit: DurationUnit): Long {\n        return toDouble(lengthUnit, durationUnit).roundToLong()\n    }\n\n    /**\n     * Returns the value of this velocity expressed as a [Double] number of the specific [lengthUnit] per\n     * [durationUnit]. Infinite values are converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY]\n     * depending on its sign.\n     */\n    public fun toDouble(lengthUnit: LengthUnit, durationUnit: DurationUnit = DurationUnit.SECONDS): Double {\n        return (rawNanometersPerSecond.toDouble() / lengthUnit.nanometerScale.toDouble()) * durationUnit.secondScale\n    }\n\n    /**\n     * Returns a fractional string representation of this velocity expressed in the specified [lengthUnit] per\n     * [durationUnit].\n     */\n    public fun toString(\n        lengthUnit: LengthUnit,\n        durationUnit: DurationUnit = DurationUnit.SECONDS,\n        decimals: Int = 0,\n    ): String {\n        return when (isInfinite()) {\n            true -> rawNanometersPerSecond.toString()\n            false -> buildString {\n                append(toDouble(lengthUnit, durationUnit).toDecimalString(decimals))\n                append(nbsp)\n                append(lengthUnit.symbol)\n                append(\"/\")\n                append(durationUnit.shortName)\n            }\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this velocity expressed in the largest [LengthUnit.International]\n     * per [second][DurationUnit.SECONDS] which is greater than or equal to 1.\n     */\n    override fun toString(): String {\n        val lengthUnit = LengthUnit.International.entries.asReversed().firstOrNull { unit ->\n            rawNanometersPerSecond.absoluteValue / unit.nanometerScale > 0\n        }\n        return toString(lengthUnit ?: LengthUnit.International.Nanometer, DurationUnit.SECONDS, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this velocity value is infinite.\n     */\n    public fun isFinite(): Boolean = rawNanometersPerSecond.isFinite()\n\n    /**\n     * Returns true if this velocity value is finite.\n     */\n    public fun isInfinite(): Boolean = rawNanometersPerSecond.isInfinite()\n\n    /**\n     * Compares this velocity with the [other] velocity. Returns zero if this velocity is equal\n     * to the specified [other] velocity, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    override fun compareTo(other: Velocity): Int = rawNanometersPerSecond.compareTo(other.rawNanometersPerSecond)\n\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/Volume.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.toDecimalString\nimport io.github.kevincianfarini.alchemist.scalar.centimeters\nimport io.github.kevincianfarini.alchemist.scalar.mm2\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport io.github.kevincianfarini.alchemist.unit.VolumeUnit\nimport kotlin.jvm.JvmInline\nimport kotlin.math.pow\nimport kotlin.math.roundToLong\n\n/**\n * Represents a measure of volume and is capable of storing ±9.2 trillion meters³ (±9.2 quadrillion liters) at\n * centimeter³ (milliliter) precision.\n */\n@JvmInline\npublic value class Volume internal constructor(private val rawCubicCentimeters: SaturatingLong) : Comparable<Volume> {\n\n    // region SI Arithmetic\n\n    /**\n     * Returns the resulting [Length] after dividing this volume over the specified [area].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this volume or\n     * [area], some precision may be lost.\n     *\n     * @throws IllegalArgumentException if both this volume and [area] are infinite.\n     */\n    public operator fun div(area: Area): Length {\n        // TODO This is simplistic and we should attempt to retain precision in the future.\n        return ((rawCubicCentimeters / area.rawMillimetersSquared) * 100).centimeters\n    }\n\n    /**\n     * Returns the resulting [Area] after dividing this volume over the specified [length].\n     *\n     * This operation attempts to retain precision, but for sufficiently large values of either this volume or\n     * [length], some precision may be lost.\n     *\n     * @throws IllegalArgumentException if both this volume and [length] are infinite.\n     */\n    public operator fun div(length: Length): Area {\n        // TODO This is simplistic and we should attempt to retain precision in the future.\n        return ((rawCubicCentimeters / length.rawNanometers) * 1_000_000_000).mm2\n    }\n\n    // endregion\n\n    // region Scalar Arithmetic\n\n    /**\n     * Returns the number that is the ratio of this and the [other] volume value.\n     *\n     * @throws IllegalArgumentException when both this and the [other] volume are [infinite][isInfinite].\n     */\n    public operator fun div(other: Volume): Double {\n        return rawCubicCentimeters.toDouble() / other.rawCubicCentimeters.toDouble()\n    }\n\n    /**\n     * Returns a volume whose value is this volume value divided by the specified [scale].\n     */\n    public operator fun div(scale: Int): Volume = div(scale.toLong())\n\n    /**\n     * Returns a volume whose value is this volume value divided by the specified [scale].\n     *\n     * @throws IllegalArgumentException when [scale] is equal to [Long.MAX_VALUE] or [Long.MIN_VALUE] and this\n     * volume is [infinite][isInfinite].\n     */\n    public operator fun div(scale: Long): Volume = Volume(rawCubicCentimeters / scale)\n\n    /**\n     * Returns the negative of this volume value.\n     */\n    public operator fun unaryMinus(): Volume = Volume(-rawCubicCentimeters)\n\n    /**\n     * Returns a volume whose value is the difference between this and the [other] volume value.\n     *\n     * @throws IllegalArgumentException if this volume and the [other] volume are both\n     * [infinite][isInfinite] but have equivalent signs.\n     */\n    public operator fun minus(other: Volume): Volume = Volume(rawCubicCentimeters - other.rawCubicCentimeters)\n\n    /**\n     * Returns a volume whose value is the sum between this and the [other] volume value.\n     *\n     * @throws IllegalArgumentException if this volume and the [other] volume are both\n     * [infinite][isInfinite] but have differing signs.\n     */\n    public operator fun plus(other: Volume): Volume = Volume(rawCubicCentimeters + other.rawCubicCentimeters)\n\n    /**\n     * Returns a volume whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this volume is [infinite][isInfinite] and [scale] is 0.\n     */\n    public operator fun times(scale: Int): Volume = times(scale.toLong())\n\n    /**\n     * Returns a volume whose value is multiplied by the specified [scale].\n     *\n     * @throws IllegalArgumentException when this volume is [infinite][isInfinite] and [scale] is 0, or when this\n     * volume is 0 and scale is [Long.MAX_VALUE] or [Long.MIN_VALUE].\n     */\n    public operator fun times(scale: Long): Volume = Volume(rawCubicCentimeters * scale)\n\n    /**\n     * Returns a volume whose value is multiplied by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this volume is [infinite][isInfinite] and [scale] is 0.0 or when this volume is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun times(scale: Double): Volume = Volume(rawCubicCentimeters * scale)\n\n    /**\n     * Returns a volume whose value is divided by the specified [scale]. This operation may be rounded when the result\n     * cannot be precisely represented with a [Double] number.\n     *\n     * @throws IllegalArgumentException when this volume is [infinite][isInfinite] and [scale] is 0.0 or when this volume is 0\n     * and scale is [infinite][Double.isInfinite].\n     */\n    public operator fun div(scale: Double): Volume = Volume(rawCubicCentimeters / scale)\n\n    // endregion\n\n    // region Volume to Scalar Conversions\n\n    /**\n     * Returns the value of this velocity expressed as a [Long] number of the specified [unit]. Infinite values\n     * are converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(unit: VolumeUnit): Long {\n        return (rawCubicCentimeters / unit.cubicCentimetersScale).rawValue\n    }\n\n    /**\n     * Returns the value of this velocity expressed as a [Long] number of the specified [LengthUnit]³. Infinite values\n     * are converted to either [Long.MAX_VALUE] or [Long.MIN_VALUE] depending on its sign.\n     */\n    public fun toLong(cubicUnit: LengthUnit): Long {\n        return toDouble(cubicUnit).roundToLong()\n    }\n\n    /**\n     * Returns the value of this volume expressed as a [Double] number of the specified [unit]. Infinite values\n     * are converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] dependeing on its sign.\n     */\n    public fun toDouble(unit: VolumeUnit): Double {\n        return rawCubicCentimeters.toDouble() / unit.cubicCentimetersScale.toDouble()\n    }\n\n    /**\n     * Returns the value of this volume expressed as a [Double] number of the specified [LengthUnit]³. Infinite values\n     * are converted to either [Double.POSITIVE_INFINITY] or [Double.NEGATIVE_INFINITY] dependeing on its sign.\n     */\n    public fun toDouble(cubicUnit: LengthUnit): Double {\n        val cubicNanos = rawCubicCentimeters.toDouble() * 1e21\n        return cubicNanos / cubicUnit.nanometerScale.toDouble().pow(3)\n    }\n\n    /**\n     * Returns a fractional string representation of this volume expressed in the specified [unit] and is rounded\n     * to the specified [decimals].\n     */\n    public fun toString(unit: VolumeUnit, decimals: Int = 0): String = when {\n        isInfinite() -> rawCubicCentimeters.toString()\n        else -> buildString {\n            append(toDouble(unit).toDecimalString(decimals))\n            append(unit.symbol)\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this volume expressed in the specified [LengthUnit]³\n     * and is rounded to the specified [decimals].\n     */\n    public fun toString(cubicUnit: LengthUnit, decimals: Int = 0): String = when {\n        isInfinite() -> rawCubicCentimeters.toString()\n        else -> buildString {\n            append(toDouble(cubicUnit).toDecimalString(decimals))\n            append(cubicUnit.symbol)\n            append(\"³\")\n        }\n    }\n\n    /**\n     * Returns a fractional string representation of this volume expressed in the largest [LengthUnit]³ quantity\n     * which is greater than or equal to 1.\n     */\n    public override fun toString(): String {\n        val lengthUnit = LengthUnit.International.entries.asReversed().firstOrNull { cubicUnit ->\n            toDouble(cubicUnit) >= 1.0\n        }\n        return toString(lengthUnit ?: LengthUnit.International.Centimeter, decimals = 2)\n    }\n\n    // endregion\n\n    // region Comparisons\n\n    /**\n     * Returns true if this volume value is infinite.\n     */\n    public fun isInfinite(): Boolean = rawCubicCentimeters.isInfinite()\n\n    /**\n     * Returns true if this volume value is finite.\n     */\n    public fun isFinite(): Boolean = rawCubicCentimeters.isFinite()\n\n    /**\n     * Compares this volume with the [other] volume. Returns zero if this volume is equal\n     * to the specified [other] volume, a negative number if it's less than [other], or a positive number\n     * if it's greater than [other].\n     */\n    override fun compareTo(other: Volume): Int = rawCubicCentimeters.compareTo(other.rawCubicCentimeters)\n\n    // endregion\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/type/duration.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport kotlin.time.Duration\n\n\n/**\n * Returns the resulting [Velocity] after multiplying this duration by the specified [acceleration].\n *\n * This operation attempts to retain precision, but for sufficiently large values of this duration or the\n * specified [acceleration] some precision may be lost.\n *\n * @throws IllegalArgumentException if this duration is infinite and [acceleration] is zero, or if this duration\n * is zero and [acceleration] is infinite.\n */\npublic operator fun Duration.times(acceleration: Acceleration): Velocity = acceleration * this\n\n/**\n * Returns the resulting [Energy] from applying the specified [power] over this duration.\n *\n * This operation attempts to retain precision, but for sufficiently large values of either this duration or [power],\n * some precision may be lost.\n *\n * @throws IllegalArgumentException if this duration is infinite and [power] is zero, or if this duration is zero and\n * [power] is infinite.\n */\npublic operator fun Duration.times(power: Power): Energy = power * this\n\n/**\n * Returns the resulting [Length] from applying the specified [velocity] over this duration.\n *\n * This operation attempts to retain precision, but for sufficiently large values of either this duration or [velocity],\n * some precision may be lost.\n *\n * @throws IllegalArgumentException if this duration is infinite and [velocity] is zero, or if this duration is zero and\n * [velocity] is infinite.\n */\npublic operator fun Duration.times(velocity: Velocity): Length = velocity * this\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/unit/AreaUnit.kt",
    "content": "package io.github.kevincianfarini.alchemist.unit\n\n\n/**\n * A unit of area precise to the millimeter².\n */\npublic interface AreaUnit {\n\n    /**\n     * The amount of millimeters² in this unit. Implementations of [AreaUnit] should be perfectly divisible by a\n     * quantity of millimeters².\n     */\n    public val millimetersSquaredScale: Long\n\n    /**\n     * The symbol of this unit.\n     */\n    public val symbol: String\n\n    /**\n     * A non-standard representation of area commonly used as part of the metric system.\n     */\n    public enum class Metric(override val symbol: String, override val millimetersSquaredScale: Long) : AreaUnit {\n        Decimilliare(\"dma\", 100),\n        Centiare(\"ca\", 1_000_000),\n        Deciare(\"da\", 10_000_000),\n        Are(\"a\", 100_000_000),\n        Decare(\"daa\", 1_000_000_000),\n        Hectare(\"ha\", 10_000_000_000),\n    }\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/unit/EnergyUnit.kt",
    "content": "package io.github.kevincianfarini.alchemist.unit\n\n/**\n * A unit of energy precise to the millijoule.\n */\npublic interface EnergyUnit {\n\n    /**\n     * The amount of millijoules in this unit. Implementations of [EnergyUnit] should be perfectly divisible by a\n     * quantity of millijoules.\n     */\n    public val millijouleScale: Long\n\n    /**\n     * The symbol of this unit.\n     */\n    public val symbol: String\n\n    /**\n     * An International System of Units standard representation of energy.\n     */\n    public enum class International(\n        override val millijouleScale: Long,\n        override val symbol: String,\n    ) : EnergyUnit {\n        Millijoule(1, \"mJ\"),\n        Joule(1_000, \"J\"),\n        Kilojoule(1_000_000, \"kJ\"),\n        Megajoule(1_000_000_000, \"MJ\"),\n        Gigajoule(1_000_000_000_000, \"GJ\"),\n        Tetrajoule(1_000_000_000_000_000, \"TJ\"),\n        Petajoule(1_000_000_000_000_000_000, \"PJ\"),\n    }\n\n    /**\n     * A non-standard representation of energy commonly used to measure electrical energy.\n     */\n    public enum class Electricity(\n        override val millijouleScale: Long,\n        override val symbol: String,\n    ) : EnergyUnit {\n        MilliwattHour(3_600, \"mWh\"),\n        WattHour(3_600_000, \"Wh\"),\n        KilowattHour(3_600_000_000, \"kWh\"),\n        MegawattHour(3_600_000_000_000, \"MWh\"),\n        GigawattHour(3_600_000_000_000_000, \"GWh\"),\n        TerawattHour(3_600_000_000_000_000_000, \"TWh\"),\n    }\n}"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/unit/ForceUnit.kt",
    "content": "package io.github.kevincianfarini.alchemist.unit\n\n/**\n * A unit of force precise to the nanonewton.\n */\npublic interface ForceUnit {\n\n    /**\n     * The symbol of this unit.\n     */\n    public val symbol: String\n\n    /**\n     * The amount of nanonewtons in this unit. Implementations of [ForceUnit] should be perfectly divisible by a\n     * quantity of nanonewtons.\n     */\n    public val nanonewtonScale: Long\n\n    /**\n     * An International System of Units standard representation of force.\n     */\n    public enum class International(override val symbol: String, override val nanonewtonScale: Long) : ForceUnit {\n        Nanonewton(\"nN\", 1),\n        Micronewton(\"μN\", 1_000),\n        Millinewton(\"mN\", 1_000_000),\n        Newton(\"N\", 1_000_000_000),\n        Kilonewton(\"kN\", 1_000_000_000_000),\n        Meganewton(\"MN\", 1_000_000_000_000_000),\n        Giganewton(\"GN\", 1_000_000_000_000_000_000)\n    }\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/unit/LengthUnit.kt",
    "content": "package io.github.kevincianfarini.alchemist.unit\n\n/**\n * A unit of length precise to the nanometer.\n */\npublic interface LengthUnit {\n\n    /**\n     * The amount of nanometers in this unit. Implementations of [LengthUnit] should be perfectly divisible by a\n     * quantity of nanometers.\n     */\n    public val nanometerScale: Long\n\n    /**\n     * The symbol of this unit.\n     */\n    public val symbol: String\n\n    /**\n     * An International System of Units standard representation of length.\n     */\n    public enum class International(\n        override val nanometerScale: Long,\n        override val symbol: String,\n    ) : LengthUnit {\n        Nanometer(1, \"nm\"),\n        Micrometer(1_000, \"μm\"),\n        Millimeter(1_000_000, \"mm\"),\n        Centimeter(10_000_000, \"cm\"),\n        Meter(1_000_000_000, \"m\"),\n        Kilometer(1_000_000_000_000, \"km\"),\n        Megameter(1_000_000_000_000_000, \"Mm\"),\n        Gigameter(1_000_000_000_000_000_000, \"Gm\"),\n    }\n\n    /**\n     * A non-standard representation of length commonly used in the United States.\n     */\n    public enum class UnitedStatesCustomary(\n        override val nanometerScale: Long,\n        override val symbol: String,\n    ) : LengthUnit {\n        Inch(25_400_000, \"in\"),\n        Foot(304_800_000, \"ft\"),\n        Yard(914_400_000, \"yd\"),\n        Mile(1_609_344_000_000, \"mi\"),\n    }\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/unit/MassUnit.kt",
    "content": "package io.github.kevincianfarini.alchemist.unit\n\n/**\n * A unit of mass precise to the microgram.\n */\npublic interface MassUnit {\n\n    /**\n     * The amount of micrograms in this unit. Implementations of [MassUnit] should be perfectly divisible by a\n     * quantity of micrograms.\n     */\n    public val microgramScale: Long\n\n    /**\n     * The symbol of this unit.\n     */\n    public val symbol: String\n\n    /**\n     * An International System of Units standard representation of mass.\n     */\n    public enum class International(override val microgramScale: Long, override val symbol: String): MassUnit {\n        Microgram(1, \"μg\"),\n        Milligram(1_000, \"mg\"),\n        Gram(1_000_000, \"g\"),\n        Kilogram(1_000_000_000, \"kg\"),\n        Megagram(1_000_000_000_000, \"Mg\"),\n        Gigagram(1_000_000_000_000_000, \"Gg\"),\n        Teragram(1_000_000_000_000_000_000, \"Tg\"),\n    }\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/unit/PowerUnit.kt",
    "content": "package io.github.kevincianfarini.alchemist.unit\n\n/**\n * A unit of power precise to the microwatt.\n */\npublic interface PowerUnit {\n\n    /**\n     * The amount of microwatts in this unit. Implementations of [PowerUnit] should be perfectly divisible by a\n     * quantity of microwatts.\n     */\n    public val microwattScale: Long\n\n    /**\n     * The symbol of this unit.\n     */\n    public val symbol: String\n\n    /**\n     * An International System of Units standard representation of power.\n     */\n    public enum class International(\n        override val microwattScale: Long,\n        override val symbol: String,\n    ) : PowerUnit {\n        Microwatt(1, \"μW\"),\n        Milliwatt(1_000, \"mW\"),\n        Watt(1_000_000, \"W\"),\n        Kilowatt(1_000_000_000, \"kW\"),\n        Megawatt(1_000_000_000_000, \"MW\"),\n        Gigawatt(1_000_000_000_000_000, \"GW\"),\n        Terawatt(1_000_000_000_000_000_000, \"TW\"),\n    }\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/unit/TemperatureUnit.kt",
    "content": "package io.github.kevincianfarini.alchemist.unit\n\nimport io.github.kevincianfarini.alchemist.internal.SaturatingLong\nimport io.github.kevincianfarini.alchemist.internal.saturated\nimport kotlin.math.roundToLong\nimport kotlin.text.Typography.nbsp\n\n/**\n * Marks [TemperatureUnit] as delicate for implementation.\n */\n@RequiresOptIn(\n    message = \"\"\"\n        Implementing TemperatureUnit requires detecting integer overflow detection, which normal Long values don't \n        expose. Implementors should exercise caution when converting between their custom temperature units and \n        nanokelvins. \n    \"\"\",\n    level = RequiresOptIn.Level.ERROR,\n)\npublic annotation class DelicateTemperatureUnit\n\n/**\n * A unit of temperature precise to the nanokelvin.\n */\n@SubclassOptInRequired(DelicateTemperatureUnit::class)\npublic interface TemperatureUnit {\n\n    /**\n     * The symbol of this unit.\n     */\n    public val symbol: String\n\n    /**\n     * Convert the degrees of this [TemperatureUnit] to nanokelvins.\n     */\n    public fun convertToNanokelvin(degrees: Long): Long\n\n    /**\n     * Convert the degrees of this [TemperatureUnit] to nanokelvins. Depending on its magnitude, some precision may be\n     * lost.\n     */\n    public fun convertToNanokelvin(degrees: Double): Long\n\n    /**\n     * Convert the amount of [nanokelvins] of this temperature in this [TemperatureUnit].\n     */\n    public fun convertNanokelvinsToThis(nanokelvins: Long): Double\n\n    /**\n     * An International System of Units standard representation of temperature.\n     */\n    public enum class International(override val symbol: String) : TemperatureUnit {\n        Nanokelvin(\"${nbsp}nK\") {\n            override fun convertToNanokelvin(degrees: Long): Long = degrees\n            override fun convertToNanokelvin(degrees: Double): Long {\n                require(!degrees.isNaN()) { \"Temperature value cannot be NaN.\" }\n                return degrees.roundToLong()\n            }\n            override fun convertNanokelvinsToThis(nanokelvins: Long): Double = nanokelvins.toDouble()\n        },\n        Microkelvin(\"${nbsp}μK\") {\n            override fun convertToNanokelvin(degrees: Long): Long = (degrees.saturated * 1_000).rawValue\n            override fun convertToNanokelvin(degrees: Double): Long {\n                val ret = degrees * 1_000\n                require(!ret.isNaN()) { \"Temperature value cannot be NaN.\" }\n                return ret.roundToLong()\n            }\n            override fun convertNanokelvinsToThis(nanokelvins: Long): Double = nanokelvins.saturated.toDouble() / 1_000\n        },\n        Millikelvin(\"${nbsp}mK\") {\n            override fun convertToNanokelvin(degrees: Long): Long = (degrees.saturated * 1_000_000).rawValue\n            override fun convertToNanokelvin(degrees: Double): Long {\n                val ret = degrees * 1_000_000\n                require(!ret.isNaN()) { \"Temperature value cannot be NaN.\" }\n                return ret.roundToLong()\n            }\n            override fun convertNanokelvinsToThis(nanokelvins: Long): Double = nanokelvins.saturated.toDouble() / 1_000_000\n        },\n        Kelvin(\"${nbsp}K\") {\n            override fun convertToNanokelvin(degrees: Long): Long = (degrees.saturated * 1_000_000_000).rawValue\n            override fun convertToNanokelvin(degrees: Double): Long {\n                val ret = degrees * 1_000_000_000\n                require(!ret.isNaN()) { \"Temperature value cannot be NaN.\" }\n                return ret.roundToLong()\n            }\n            override fun convertNanokelvinsToThis(nanokelvins: Long): Double = nanokelvins.saturated.toDouble() / 1_000_000_000\n        },\n        Kilokelvin(\"${nbsp}kK\") {\n            override fun convertToNanokelvin(degrees: Long): Long = (degrees.saturated * 1_000_000_000_000).rawValue\n            override fun convertToNanokelvin(degrees: Double): Long {\n                val ret = degrees * 1_000_000_000_000\n                require(!ret.isNaN()) { \"Temperature value cannot be NaN.\" }\n                return ret.roundToLong()\n            }\n            override fun convertNanokelvinsToThis(nanokelvins: Long): Double = nanokelvins.saturated.toDouble() / 1_000_000_000_000\n        },\n        Megakelvin(\"${nbsp}MK\") {\n            override fun convertToNanokelvin(degrees: Long): Long = (degrees.saturated * 1_000_000_000_000_000).rawValue\n            override fun convertToNanokelvin(degrees: Double): Long {\n                val ret = degrees * 1_000_000_000_000_000\n                require(!ret.isNaN()) { \"Temperature value cannot be NaN.\" }\n                return ret.roundToLong()\n            }\n            override fun convertNanokelvinsToThis(nanokelvins: Long): Double = nanokelvins.saturated.toDouble() / 1_000_000_000_000_000\n        },\n        Gigakelvin(\"${nbsp}GK\") {\n            override fun convertToNanokelvin(degrees: Long): Long = (degrees.saturated * 1_000_000_000_000_000_000).rawValue\n            override fun convertToNanokelvin(degrees: Double): Long {\n                val ret = degrees * 1_000_000_000_000_000_000\n                require(!ret.isNaN()) { \"Temperature value cannot be NaN.\" }\n                return ret.roundToLong()\n            }\n            override fun convertNanokelvinsToThis(nanokelvins: Long): Double = nanokelvins.saturated.toDouble() / 1_000_000_000_000_000_000\n        },\n        Celsius(\"°C\") {\n            override fun convertToNanokelvin(degrees: Long): Long {\n                return ((degrees.saturated * 1_000_000_000) + 273_150_000_000).rawValue\n            }\n            override fun convertToNanokelvin(degrees: Double): Long {\n                val ret = (degrees * 1_000_000_000) + 273_150_000_000\n                require(!ret.isNaN()) { \"Temperature value cannot be NaN.\" }\n                return ret.roundToLong()\n            }\n            override fun convertNanokelvinsToThis(nanokelvins: Long): Double {\n                return (nanokelvins.saturated.toDouble() - 273_150_000_000) / 1_000_000_000\n            }\n        },\n    }\n\n    /**\n     * A non-standard unit of temperature used in the United States.\n     */\n    public object Fahrenheit : TemperatureUnit {\n        override val symbol: String get() = \"°F\"\n        override fun convertToNanokelvin(degrees: Long): Long {\n            val accurate = (((degrees.saturated * 1_000_000_000) + 459_670_000_000) * 5) / 9\n            return if (accurate.isFinite()) {\n                accurate.rawValue\n            } else {\n                ((degrees.saturated * 555_555_556) + 255_372_222_222).rawValue\n            }\n        }\n\n        override fun convertToNanokelvin(degrees: Double): Long {\n            val ret = (((degrees * 1_000_000_000) + 459_670_000_000) * 5) / 9\n            require(!ret.isNaN()) { \"Temperature value cannot be NaN.\" }\n            return ret.roundToLong()\n        }\n        override fun convertNanokelvinsToThis(nanokelvins: Long): Double {\n            return (nanokelvins.saturated - 255_372_222_222).toDouble() / 555_555_556.toDouble()\n        }\n    }\n}\n\ninternal fun TemperatureUnit.convertToNanokelvin(value: SaturatingLong): SaturatingLong {\n    return convertToNanokelvin(value.rawValue).saturated\n}\n\ninternal fun TemperatureUnit.convertNanokelvinsToThis(nanokelvins: SaturatingLong): Double {\n    return convertNanokelvinsToThis(nanokelvins.rawValue)\n}\n"
  },
  {
    "path": "src/commonMain/kotlin/io/github/kevincianfarini/alchemist/unit/VolumeUnit.kt",
    "content": "package io.github.kevincianfarini.alchemist.unit\n\nimport kotlin.text.Typography.nbsp\n\n/**\n * A unit of volume precise to the centimeter³.\n */\npublic interface VolumeUnit {\n\n    /**\n     * The symbol of this unit.\n     */\n    public val symbol: String\n\n    /**\n     * The amount of centimeter³ in this unit. Implementations of [TemperatureUnit] should be perfectly divisible by a\n     * quantity of centimeter³.\n     */\n    public val cubicCentimetersScale: Long\n\n    /**\n     * A non-standard representation of volume commonly used as part of the metric system.\n     */\n    public enum class Metric(override val symbol: String, override val cubicCentimetersScale: Long) : VolumeUnit {\n        Milliliter(\"${nbsp}mL\", 1),\n        Liter(\"${nbsp}L\", 1_000),\n        Kiloliter(\"${nbsp}kL\", 1_000_000),\n        Megaliter(\"${nbsp}ML\", 1_000_000_000),\n        Gigaliter(\"${nbsp}GL\", 1_000_000_000_000),\n        Teraliter(\"${nbsp}TL\", 1_000_000_000_000_000),\n        Petaliter(\"${nbsp}PL\", 1_000_000_000_000_000_000),\n    }\n}\n"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/Long.kt",
    "content": "package io.github.kevincianfarini.alchemist\n\n/**\n * Convert this [Long] to its binary string representation. The result is chunked into 8 bytes. This function is marked\n * as unused, but it's useful for debugging.\n *\n * The following example:\n *\n * ```kt\n * println(Long.MAX_VALUE.toBinaryString())\n * println(Long.MIN_VALUE.toBinaryString())\n * ```\n *\n * will print the following results:\n *\n * ```\n * 01111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111\n * 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000\n * ```\n */\n@Suppress(\"unused\")\nfun Long.toBinaryString(): String = toULong()\n    .toString(radix = 2)\n    .padStart(64, '0')\n    .chunked(8)\n    .joinToString(\" \")"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/ignore.kt",
    "content": "@file:OptIn(ExperimentalMultiplatform::class)\n\npackage io.github.kevincianfarini.alchemist\n\n/**\n * Ignores a test for the Js target.\n */\n@OptionalExpectation\nexpect annotation class JsIgnore()\n\n/**\n * Ignores a test for the WasmJs target.\n */\n@OptionalExpectation\nexpect annotation class WasmJsIgnore()\n\n/**\n * Ignores a test for the WasmWasi target.\n */\n@OptionalExpectation\nexpect annotation class WasmWasiIgnore()"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/internal/DoubleTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\nimport io.github.kevincianfarini.alchemist.WasmWasiIgnore\nimport kotlin.test.assertEquals\nimport kotlin.test.Test\n\nclass DoubleTest {\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun exact_omits_decimal_point() {\n        assertEquals(\n            expected = \"0\",\n            actual = 0.0.toDecimalString(0),\n        )\n    }\n\n    @Test\n    fun decimals_rounded_up() {\n        assertEquals(\n            expected = \"123.457\",\n            actual = 123.4566.toDecimalString(3),\n        )\n    }\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun pads_with_zeros() {\n        assertEquals(\n            expected = \"123.4565000000\",\n            actual = 123.4565.toDecimalString(10),\n        )\n    }\n\n    @Test\n    fun negative_sign() {\n        assertEquals(\n            expected = \"-123.457\",\n            actual = (-123.4566).toDecimalString(3)\n        )\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/internal/SaturatingLongTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\nimport io.github.kevincianfarini.alchemist.WasmJsIgnore\nimport io.github.kevincianfarini.alchemist.WasmWasiIgnore\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFails\nimport kotlin.test.assertFailsWith\nimport kotlin.test.assertTrue\n\nclass SaturatingLongTest {\n\n    @Test\n    fun long_max_and_min_value_is_infinite() {\n        assertTrue(Long.MAX_VALUE.saturated.isInfinite())\n        assertTrue(Long.MIN_VALUE.saturated.isInfinite())\n    }\n\n    @Test\n    fun unary_minus_works() {\n        assertEquals(1L.saturated, -((-1L).saturated))\n    }\n\n    @Test\n    fun unary_minus_infinite_values() {\n        assertEquals(POSITIVE_INFINITY, -NEGATIVE_INFINITY)\n        assertEquals(NEGATIVE_INFINITY, -POSITIVE_INFINITY)\n    }\n\n    @Test\n    fun infinite_plus_infinite_equals_infinite() {\n        assertEquals(\n            expected = POSITIVE_INFINITY,\n            actual = POSITIVE_INFINITY + POSITIVE_INFINITY,\n        )\n        assertEquals(\n            expected = NEGATIVE_INFINITY,\n            actual = NEGATIVE_INFINITY+ NEGATIVE_INFINITY,\n        )\n    }\n\n    @Test\n    fun mixed_signed_infinite_addition_error() {\n        val e = assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY + NEGATIVE_INFINITY\n        }\n        assertEquals(\n            actual = e.message,\n            expected = \"Summing infinite values of different signs yields an undefined result.\",\n        )\n    }\n\n    @Test\n    fun addition_positive_overflow_returns_positive_infinite() {\n        val a = (Long.MAX_VALUE - 1L).saturated\n        val b = 2L.saturated\n        assertEquals(POSITIVE_INFINITY, a + b)\n    }\n\n    @Test\n    fun addition_negative_overflow_returns_negative_infinite() {\n        val a = (Long.MIN_VALUE + 1L).saturated\n        val b = (-2L).saturated\n        assertEquals(NEGATIVE_INFINITY, a + b)\n    }\n\n    @Test\n    fun subtraction_negative_overflow_returns_negative_infinite() {\n        val a = (Long.MIN_VALUE + 1L).saturated\n        val b = 2L.saturated\n        assertEquals(NEGATIVE_INFINITY, a - b)\n    }\n\n    @Test\n    fun subtraction_positive_overflow_returns_positive_infinite() {\n        val a = -((Long.MAX_VALUE - 1L).saturated)\n        val b = 2L.saturated\n        assertEquals(POSITIVE_INFINITY, b - a)\n    }\n\n    @Test\n    fun adding_to_infinity_produces_infinity() {\n        assertEquals(POSITIVE_INFINITY, POSITIVE_INFINITY + 50_000L.saturated)\n    }\n\n    @Test\n    fun adding_to_negative_infinity_produces_negative_infinity() {\n        assertEquals(NEGATIVE_INFINITY, NEGATIVE_INFINITY + 50_000L.saturated)\n    }\n\n    @Test\n    fun subtracting_from_positive_infinity_produces_positive_infinity() {\n        assertEquals(POSITIVE_INFINITY, POSITIVE_INFINITY - 50_000L.saturated)\n    }\n\n    @Test\n    fun subtracting_from_negative_infinity_produces_negative_infinity() {\n        assertEquals(NEGATIVE_INFINITY, NEGATIVE_INFINITY - 50_000L.saturated)\n    }\n\n    @Test\n    fun dividing_works() {\n        assertEquals(2L.saturated, 10L.saturated / 5L.saturated)\n    }\n\n    @Test\n    fun dividing_two_infinite_values_errors() {\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY / POSITIVE_INFINITY\n        }\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY / NEGATIVE_INFINITY\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY / POSITIVE_INFINITY\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY / NEGATIVE_INFINITY\n        }\n    }\n\n    @Test\n    fun dividing_by_positive_infinity_produces_zero() {\n        assertEquals(0L.saturated, (Long.MAX_VALUE - 1L).saturated / POSITIVE_INFINITY)\n    }\n\n    @Test\n    fun dividing_by_negative_infinity_produces_zero() {\n        assertEquals(0L.saturated, (Long.MAX_VALUE - 1L).saturated / NEGATIVE_INFINITY)\n    }\n\n    @Test\n    fun dividing_positive_infinity_by_a_positive_value_produces_positive_infinity() {\n        assertEquals(POSITIVE_INFINITY, POSITIVE_INFINITY / 2L)\n    }\n\n    @Test\n    fun dividing_positive_infinity_by_negative_value_produces_negative_infinity() {\n        assertEquals(NEGATIVE_INFINITY, POSITIVE_INFINITY / -1L)\n    }\n\n    @Test\n    fun dividing_negative_infinity_by_a_positive_value_produces_negative_infinity() {\n        assertEquals(NEGATIVE_INFINITY, NEGATIVE_INFINITY / 2L)\n    }\n\n    @Test\n    fun dividing_negative_infinity_by_negative_value_produces_positive_infinity() {\n        assertEquals(POSITIVE_INFINITY, NEGATIVE_INFINITY / -1L)\n    }\n\n    @Test\n    fun dividing_finite_value_by_infinite_value_produces_zero() {\n        assertEquals(0L.saturated, (Long.MAX_VALUE - 1).saturated / POSITIVE_INFINITY)\n        assertEquals(0L.saturated, (Long.MAX_VALUE - 1).saturated / NEGATIVE_INFINITY)\n        assertEquals(0L.saturated, (Long.MIN_VALUE + 1).saturated / POSITIVE_INFINITY)\n        assertEquals(0L.saturated, (Long.MIN_VALUE + 1).saturated / NEGATIVE_INFINITY)\n    }\n\n    @Test\n    fun multiplication_works() {\n        assertEquals(10L.saturated, 5L.saturated * 2L.saturated)\n    }\n\n    @Test\n    fun multiplfying_two_positive_infinite_values_produces_positive_infinity() {\n        assertEquals(POSITIVE_INFINITY, POSITIVE_INFINITY * POSITIVE_INFINITY)\n    }\n\n    @Test\n    fun multiplying_two_negative_infinite_values_produces_positive_infinity() {\n        assertEquals(POSITIVE_INFINITY, NEGATIVE_INFINITY * NEGATIVE_INFINITY)\n    }\n\n    @Test\n    fun multiplying_mixed_sign_infinite_values_produces_negative_infinity() {\n        assertEquals(NEGATIVE_INFINITY, POSITIVE_INFINITY * NEGATIVE_INFINITY)\n        assertEquals(NEGATIVE_INFINITY, NEGATIVE_INFINITY * POSITIVE_INFINITY)\n    }\n\n    @Test\n    fun two_large_overflowing_positive_numbers_produces_positive_infinity() {\n        val a = (Long.MAX_VALUE / 3).saturated\n        val b = (Long.MAX_VALUE / 4).saturated\n        assertEquals(POSITIVE_INFINITY, a * b)\n    }\n\n    @Test\n    fun two_large_overflowing_negative_numbers_produces_positive_infinity() {\n        val a = (Long.MIN_VALUE / 3).saturated\n        val b = (Long.MIN_VALUE / 4).saturated\n        assertEquals(POSITIVE_INFINITY, a * b)\n    }\n\n    @Test\n    fun two_large_overflowing_mixed_sign_numbers_produces_negative_infinity() {\n        val a = (Long.MAX_VALUE / 3).saturated\n        val b = (Long.MIN_VALUE / 4).saturated\n        assertEquals(NEGATIVE_INFINITY, a * b)\n    }\n\n    @Test\n    fun aboslute_value_infinity_produces_infinity() {\n        assertEquals(POSITIVE_INFINITY, POSITIVE_INFINITY.absoluteValue)\n        assertEquals(POSITIVE_INFINITY, NEGATIVE_INFINITY.absoluteValue)\n    }\n\n    @Test\n    fun absolute_value_works() {\n        assertEquals(10L.saturated, (-10L).saturated.absoluteValue)\n        assertEquals(10L.saturated, 10L.saturated.absoluteValue)\n    }\n\n    @Test\n    fun infinity_times_infinity_preserves_sign() {\n        assertEquals(POSITIVE_INFINITY, POSITIVE_INFINITY * POSITIVE_INFINITY)\n        assertEquals(NEGATIVE_INFINITY, NEGATIVE_INFINITY * POSITIVE_INFINITY)\n        assertEquals(NEGATIVE_INFINITY, POSITIVE_INFINITY * NEGATIVE_INFINITY)\n        assertEquals(POSITIVE_INFINITY, NEGATIVE_INFINITY * NEGATIVE_INFINITY)\n    }\n\n    @Test\n    fun infinity_times_zero_throws() {\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY * 0\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY * 0\n        }\n    }\n\n    @Test\n    fun does_not_overflow_mul_negative_one() {\n        assertEquals(\n            expected = (-1_000_000_000_000_000_000).saturated,\n            actual = (-1L).saturated * 1_000_000_000_000_000_000,\n        )\n    }\n\n    @Test\n    fun multiplication_overflow_does_not_divide_by_zero() {\n        assertEquals(0L.saturated, 0L.saturated * (Long.MAX_VALUE - 1))\n    }\n\n    @Test\n    fun multiplying_by_nan_throws() {\n        assertFailsWith<IllegalArgumentException> {\n            10L.saturated * Double.NaN\n        }\n    }\n\n    @Test\n    fun multiplying_by_double_infinity_preserves_sign() {\n        assertEquals(POSITIVE_INFINITY, 10L.saturated * Double.POSITIVE_INFINITY)\n        assertEquals(NEGATIVE_INFINITY, 10L.saturated * Double.NEGATIVE_INFINITY)\n        assertEquals(NEGATIVE_INFINITY, (-10L).saturated * Double.POSITIVE_INFINITY)\n        assertEquals(POSITIVE_INFINITY, (-10L).saturated * Double.NEGATIVE_INFINITY)\n    }\n\n    @Test\n    fun multiplying_infinity_by_double_preserves_sign() {\n        assertEquals(POSITIVE_INFINITY, POSITIVE_INFINITY * 2.0)\n        assertEquals(NEGATIVE_INFINITY, NEGATIVE_INFINITY * 2.0)\n        assertEquals(NEGATIVE_INFINITY, POSITIVE_INFINITY * -2.0)\n        assertEquals(POSITIVE_INFINITY, NEGATIVE_INFINITY * -2.0)\n    }\n\n    @Test\n    fun multiplying_by_double_zero_throws_for_infinity() {\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY * 0.0\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY * 0.0\n        }\n    }\n\n    @Test\n    fun multiplying_by_double_overflows_correctly() {\n        assertEquals(POSITIVE_INFINITY, (Long.MAX_VALUE / 2).saturated * 2.1)\n        assertEquals(NEGATIVE_INFINITY, (Long.MAX_VALUE / 2).saturated * -2.1)\n        assertEquals(NEGATIVE_INFINITY, (Long.MIN_VALUE / 2).saturated * 2.1)\n        assertEquals(POSITIVE_INFINITY, (Long.MIN_VALUE / 2).saturated * -2.1)\n    }\n\n    @Test\n    fun multiplying_by_double_uses_integer_path_when_possible() {\n        val bigLong = 92233720368547758L\n        assertEquals((bigLong * 2L).saturated, bigLong.saturated * 2.0)\n    }\n\n    @Test\n    fun multiplying_by_double_uses_float_path_when_necessary() {\n      val bigLong = 92233720368547758L\n      assertEquals((bigLong.toDouble() * 0.3).toLong().saturated, bigLong.saturated * 0.3)\n    }\n\n    @Test\n    fun dividing_by_double_nan_throws() {\n        assertFailsWith<IllegalArgumentException> {\n            10L.saturated / Double.NaN\n        }\n    }\n\n    @Test\n    fun dividing_by_double_infinity_preserves_sign() {\n        assertEquals(0L.saturated, 10L.saturated / Double.POSITIVE_INFINITY)\n        assertEquals(0L.saturated, 10L.saturated / Double.NEGATIVE_INFINITY)\n        assertEquals(0L.saturated, (-10L).saturated / Double.POSITIVE_INFINITY)\n        assertEquals(0L.saturated, (-10L).saturated / Double.NEGATIVE_INFINITY)\n    }\n\n    @Test\n    fun dividing_infinity_by_double_preserves_sign() {\n        assertEquals(POSITIVE_INFINITY, POSITIVE_INFINITY / 2.0)\n        assertEquals(NEGATIVE_INFINITY, NEGATIVE_INFINITY / 2.0)\n        assertEquals(NEGATIVE_INFINITY, POSITIVE_INFINITY / -2.0)\n        assertEquals(POSITIVE_INFINITY, NEGATIVE_INFINITY / -2.0)\n    }\n\n    @Test\n    @WasmWasiIgnore\n    @WasmJsIgnore // See: https://youtrack.jetbrains.com/issue/KT-66081.\n    fun dividing_by_double_zero_throws() {\n        assertFails {\n            // Kotlin/JS doesn't throw ArithmeticException for Long.div(0). Don't try to assert against a specific\n            // exception type here.\n            //\n            // See: https://github.com/JetBrains/kotlin/blob/62cfeef19de48b908e8abbd835422cdb1192576c/libraries/stdlib/js/runtime/longJs.kt#L217.\n            10L.saturated / 0.0\n        }\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY / 0.0\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY / 0.0\n        }\n    }\n\n    @Test\n    fun dividing_by_double_uses_integer_path_when_possible() {\n        val bigLong = 92233720368547758L\n        assertEquals((bigLong / 2L).saturated, bigLong.saturated / 2.0)\n    }\n\n    @Test\n    fun dividing_by_double_uses_float_path_when_necessary() {\n        val bigLong = 92233720368547758L\n        assertEquals((bigLong.toDouble() / 0.3).toLong().saturated, bigLong.saturated / 0.3)\n    }\n}\n"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/AccelerationTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.WasmWasiIgnore\nimport io.github.kevincianfarini.alchemist.scalar.grams\nimport io.github.kevincianfarini.alchemist.scalar.nanonewtons\nimport io.github.kevincianfarini.alchemist.scalar.nmPerSecond\nimport io.github.kevincianfarini.alchemist.scalar.nmPerSecond2\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.text.Typography.nbsp\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.DurationUnit\n\nclass AccelerationTest {\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun simple() {\n        assertEquals(\n            expected = \"1.00${nbsp}m/s²\",\n            actual = 1_000_000_000.nmPerSecond2.toString()\n        )\n    }\n\n    @Test\n    fun less_simple() {\n        assertEquals(\n            expected = \"8052.97${nbsp}mi/h²\",\n            actual = 1_000_000_000.nmPerSecond2.toString(\n                lengthUnit = LengthUnit.UnitedStatesCustomary.Mile,\n                durationUnit = DurationUnit.HOURS,\n                decimals = 2,\n            )\n        )\n    }\n\n    @Test\n    fun acceleration_mul_time_simple() {\n        assertEquals(\n            expected = 12_340_000_000.nmPerSecond,\n            actual = 10_000_000_000.nmPerSecond2 * 1_234.milliseconds,\n        )\n    }\n\n    @Test\n    fun acceleration_mul_mass_simple() {\n        // 152345677.626 nanonewtons, but we lose precision.\n        assertEquals(\n            expected = 152_345_677.nanonewtons,\n            actual = 123_456_789.nmPerSecond2 * 1_234.grams,\n        )\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/AreaTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.WasmWasiIgnore\nimport io.github.kevincianfarini.alchemist.internal.NEGATIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.internal.POSITIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.scalar.kilometers\nimport io.github.kevincianfarini.alchemist.scalar.milliliters\nimport io.github.kevincianfarini.alchemist.scalar.millimeters\nimport io.github.kevincianfarini.alchemist.scalar.mm2\nimport io.github.kevincianfarini.alchemist.scalar.nanometers\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\nimport kotlin.test.assertFalse\nimport kotlin.test.assertTrue\n\nclass AreaTest {\n\n    @Test\n    fun infinite_area_div_finite_length_is_infinite() {\n        assertEquals(POSITIVE_INFINITY.nanometers, POSITIVE_INFINITY.mm2 / 1.nanometers)\n        assertEquals(NEGATIVE_INFINITY.nanometers, POSITIVE_INFINITY.mm2 / (-1).nanometers)\n        assertEquals(NEGATIVE_INFINITY.nanometers, NEGATIVE_INFINITY.mm2 / 1.nanometers)\n        assertEquals(POSITIVE_INFINITY.nanometers, NEGATIVE_INFINITY.mm2 / (-1).nanometers)\n    }\n\n    @Test\n    fun finite_area_div_infinite_length_is_zero() {\n        assertEquals(0.nanometers, (Long.MAX_VALUE - 1).mm2 / POSITIVE_INFINITY.nanometers)\n        assertEquals(0.nanometers, (Long.MAX_VALUE - 1).mm2 / NEGATIVE_INFINITY.nanometers)\n        assertEquals(0.nanometers, (Long.MIN_VALUE + 1).mm2 / POSITIVE_INFINITY.nanometers)\n        assertEquals(0.nanometers, (Long.MIN_VALUE + 1).mm2 / NEGATIVE_INFINITY.nanometers)\n    }\n\n    @Test\n    fun infinite_area_div_infinite_length_throws() {\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY.mm2 / POSITIVE_INFINITY.nanometers\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY.mm2 / POSITIVE_INFINITY.nanometers\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY.mm2 / NEGATIVE_INFINITY.nanometers\n        }\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY.mm2 / NEGATIVE_INFINITY.nanometers\n        }\n    }\n\n    @Test\n    fun nano2_precision_div_length_is_accurate() {\n        assertEquals(123_000_000_000_000.nanometers, 123.mm2 / 1.nanometers)\n    }\n\n    @Test\n    fun micro2_precision_div_length_is_accurate() {\n        assertEquals(9_223_373.millimeters, 9_223_373.mm2 / 1.millimeters)\n    }\n\n    @Test\n    fun milli2_precision_div_length_is_accurate() {\n        assertEquals(1.kilometers, 9_223_373_000_000.mm2 / 9_223_373_000_000.nanometers)\n    }\n\n    @Test\n    fun area_div_scale_is_correct() {\n        assertEquals(1.mm2, 2.mm2 / 2)\n        assertEquals(0.mm2, 1.mm2 / 2)\n        assertEquals(POSITIVE_INFINITY.mm2, POSITIVE_INFINITY.mm2 / 2)\n        assertEquals(NEGATIVE_INFINITY.mm2, POSITIVE_INFINITY.mm2 / -2)\n        assertEquals(NEGATIVE_INFINITY.mm2, NEGATIVE_INFINITY.mm2 / 2)\n        assertEquals(POSITIVE_INFINITY.mm2, NEGATIVE_INFINITY.mm2 / -2)\n    }\n\n    @Test\n    fun area_div_area_is_correct() {\n        assertEquals(1.0, 1.mm2 / 1.mm2)\n        assertEquals(2.5, 5.mm2 / 2.mm2)\n        assertEquals(Double.POSITIVE_INFINITY, POSITIVE_INFINITY.mm2 / 2.mm2)\n        assertEquals(Double.NEGATIVE_INFINITY, NEGATIVE_INFINITY.mm2 / 2.mm2)\n        assertEquals(Double.NEGATIVE_INFINITY, POSITIVE_INFINITY.mm2 / (-2).mm2)\n        assertEquals(Double.POSITIVE_INFINITY, NEGATIVE_INFINITY.mm2 / (-2).mm2)\n    }\n\n    @Test\n    fun area_minus_area_is_correct() {\n        assertEquals((-1).mm2, 0.mm2 - 1.mm2)\n        assertEquals(5.mm2, 10.mm2 - 5.mm2)\n        assertEquals(12.mm2, 10.mm2 - (-2).mm2)\n        assertEquals(NEGATIVE_INFINITY.mm2, (Long.MIN_VALUE + 1).mm2 - 1.mm2)\n        assertEquals(POSITIVE_INFINITY.mm2, (Long.MAX_VALUE - 1).mm2 - (-1).mm2)\n        assertEquals(POSITIVE_INFINITY.mm2, POSITIVE_INFINITY.mm2 - 100_000_000.mm2)\n        assertEquals(NEGATIVE_INFINITY.mm2, NEGATIVE_INFINITY.mm2 - 100_000_000.mm2)\n    }\n\n    @Test\n    fun area_plus_area_is_correct() {\n        assertEquals(1.mm2, 0.mm2 + 1.mm2)\n        assertEquals(15.mm2, 10.mm2 + 5.mm2)\n        assertEquals(8.mm2, 10.mm2 + (-2).mm2)\n        assertEquals(NEGATIVE_INFINITY.mm2, (Long.MIN_VALUE + 1).mm2 + (-1).mm2)\n        assertEquals(POSITIVE_INFINITY.mm2, (Long.MAX_VALUE - 1).mm2 + 1.mm2)\n        assertEquals(POSITIVE_INFINITY.mm2, POSITIVE_INFINITY.mm2 + 100_000_000.mm2)\n        assertEquals(NEGATIVE_INFINITY.mm2, NEGATIVE_INFINITY.mm2 + 100_000_000.mm2)\n    }\n\n    @Test\n    fun area_mul_scale_is_correct() {\n        assertEquals(4.mm2, 2.mm2 * 2)\n        assertEquals(0.mm2, 0.mm2 * 2)\n        assertEquals(POSITIVE_INFINITY.mm2, POSITIVE_INFINITY.mm2 * 2)\n        assertEquals(NEGATIVE_INFINITY.mm2, POSITIVE_INFINITY.mm2 * -2)\n        assertEquals(NEGATIVE_INFINITY.mm2, NEGATIVE_INFINITY.mm2 * 2)\n        assertEquals(POSITIVE_INFINITY.mm2, NEGATIVE_INFINITY.mm2 * -2)\n    }\n\n    @Test\n    fun area_to_double_is_correct() {\n        val scale = 123_456_789_987_654_321\n        val area = scale.mm2\n        assertEquals(scale.toDouble(), area.toDouble(LengthUnit.International.Millimeter))\n        assertEquals(scale.toDouble() / 1_000_000, area.toDouble(LengthUnit.International.Meter))\n        assertEquals(scale.toDouble() / 1_000_000_000_000, area.toDouble(LengthUnit.International.Kilometer))\n        assertEquals(scale.toDouble() / 1_000_000_000_000_000_000, area.toDouble(LengthUnit.International.Megameter))\n    }\n\n    @Test\n    fun to_international_components_works() {\n        val mega = 1_000_000_000_000_000_000.mm2\n        val kilo = 1_000_000_000_000.mm2\n        val meters = 1_000_000.mm2\n        val centi = 100.mm2\n        val milli = 1.mm2\n        val area = mega + kilo + meters + centi + milli\n        area.toInternationalComponents { megametersSquared, kilometersSquared, metersSquared, centimetersSquared, millimetersSquared ->\n            assertEquals(1, megametersSquared)\n            assertEquals(1, kilometersSquared)\n            assertEquals(1, metersSquared)\n            assertEquals(1, centimetersSquared)\n            assertEquals(1, millimetersSquared)\n        }\n    }\n\n    @Test\n    fun infinity_to_international_components_works() {\n        POSITIVE_INFINITY.mm2.toInternationalComponents { megametersSquared, kilometersSquared, metersSquared, centimetersSquared, millimetersSquared ->\n            assertEquals(megametersSquared, Long.MAX_VALUE)\n            assertEquals(kilometersSquared, Long.MAX_VALUE)\n            assertEquals(metersSquared, Long.MAX_VALUE)\n            assertEquals(centimetersSquared, Long.MAX_VALUE)\n            assertEquals(millimetersSquared, Long.MAX_VALUE)\n        }\n        NEGATIVE_INFINITY.mm2.toInternationalComponents { megametersSquared, kilometersSquared, metersSquared, centimetersSquared, millimetersSquared ->\n            assertEquals(megametersSquared, Long.MIN_VALUE)\n            assertEquals(kilometersSquared, Long.MIN_VALUE)\n            assertEquals(metersSquared, Long.MIN_VALUE)\n            assertEquals(centimetersSquared, Long.MIN_VALUE)\n            assertEquals(millimetersSquared, Long.MIN_VALUE)\n        }\n    }\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun to_string_override_works() {\n        assertEquals(\"0.00mm²\", 0.mm2.toString())\n        assertEquals(\"1.00mm²\", 1.mm2.toString())\n        assertEquals(\"1.00cm²\", 100.mm2.toString())\n        assertEquals(\"1.01cm²\", 101.mm2.toString())\n        assertEquals(\"1.00m²\", 1_000_000.mm2.toString())\n        assertEquals(\"1.01m²\", 1_010_000.mm2.toString())\n        assertEquals(\"1.00km²\", 1_000_000_000_000.mm2.toString())\n        assertEquals(\"1.01km²\", 1_010_000_000_000.mm2.toString())\n        assertEquals(\"1.00Mm²\", 1_000_000_000_000_000_000.mm2.toString())\n        assertEquals(\"1.01Mm²\", 1_010_000_000_000_000_000.mm2.toString())\n        assertEquals(\"Infinity\", POSITIVE_INFINITY.mm2.toString())\n        assertEquals(\"-Infinity\", NEGATIVE_INFINITY.mm2.toString())\n    }\n\n    @Test\n    fun compare_to_works() {\n        assertTrue(1.mm2 < 2.mm2)\n        assertTrue(2.mm2 <= 2.mm2)\n        assertFalse(1.mm2 > 2.mm2)\n        assertFalse(1.mm2 >= 2.mm2)\n        assertFalse(POSITIVE_INFINITY.mm2 > POSITIVE_INFINITY.mm2)\n        assertFalse(POSITIVE_INFINITY.mm2 < POSITIVE_INFINITY.mm2)\n        assertTrue(POSITIVE_INFINITY.mm2 >= POSITIVE_INFINITY.mm2)\n        assertTrue(POSITIVE_INFINITY.mm2 <= POSITIVE_INFINITY.mm2)\n        assertTrue(POSITIVE_INFINITY.mm2 > NEGATIVE_INFINITY.mm2)\n        assertTrue(POSITIVE_INFINITY.mm2 >= NEGATIVE_INFINITY.mm2)\n        assertFalse(NEGATIVE_INFINITY.mm2 > NEGATIVE_INFINITY.mm2)\n        assertFalse(NEGATIVE_INFINITY.mm2 < NEGATIVE_INFINITY.mm2)\n        assertTrue(NEGATIVE_INFINITY.mm2 >= NEGATIVE_INFINITY.mm2)\n        assertTrue(NEGATIVE_INFINITY.mm2 <= NEGATIVE_INFINITY.mm2)\n        assertTrue(NEGATIVE_INFINITY.mm2 < POSITIVE_INFINITY.mm2)\n        assertTrue(NEGATIVE_INFINITY.mm2 <= POSITIVE_INFINITY.mm2)\n    }\n\n    @Test\n    fun is_finite_works() {\n        assertTrue(1.mm2.isFinite())\n        assertTrue((Long.MAX_VALUE - 1).mm2.isFinite())\n        assertTrue((Long.MIN_VALUE + 1).mm2.isFinite())\n        assertFalse(Long.MAX_VALUE.mm2.isFinite())\n        assertFalse(Long.MIN_VALUE.mm2.isFinite())\n        assertFalse(POSITIVE_INFINITY.mm2.isFinite())\n        assertFalse(NEGATIVE_INFINITY.mm2.isFinite())\n    }\n\n    @Test\n    fun area_mul_length_simple() {\n        assertEquals(\n            expected = 1_219_251.milliliters,\n            actual = 123_456_000_000.mm2 * 9_876.nanometers,\n        )\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/EnergyTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.NEGATIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.internal.POSITIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.scalar.gigajoules\nimport io.github.kevincianfarini.alchemist.scalar.gigawattHours\nimport io.github.kevincianfarini.alchemist.scalar.joules\nimport io.github.kevincianfarini.alchemist.scalar.kilojoules\nimport io.github.kevincianfarini.alchemist.scalar.kilowattHours\nimport io.github.kevincianfarini.alchemist.scalar.megajoules\nimport io.github.kevincianfarini.alchemist.scalar.megawattHours\nimport io.github.kevincianfarini.alchemist.scalar.microwatts\nimport io.github.kevincianfarini.alchemist.scalar.millijoules\nimport io.github.kevincianfarini.alchemist.scalar.milliwattHours\nimport io.github.kevincianfarini.alchemist.scalar.petajoules\nimport io.github.kevincianfarini.alchemist.scalar.terawattHours\nimport io.github.kevincianfarini.alchemist.scalar.tetrajoules\nimport io.github.kevincianfarini.alchemist.scalar.wattHours\nimport io.github.kevincianfarini.alchemist.scalar.watts\nimport io.github.kevincianfarini.alchemist.unit.EnergyUnit\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.hours\nimport kotlin.time.Duration.Companion.microseconds\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.nanoseconds\nimport kotlin.time.Duration.Companion.seconds\n\nclass EnergyTest {\n\n    @Test\n    fun to_international_components_works() {\n        val energy = 1.petajoules + 1.tetrajoules + 1.gigajoules + 1.megajoules + 1.kilojoules + 1.joules + 1.millijoules\n        energy.toInternationalComponents { petajoules, tetrajoules, gigajoules, megajoules, kilojoules, joules, millijoules ->\n            assertEquals(1L, petajoules)\n            assertEquals(1L, tetrajoules)\n            assertEquals(1L, gigajoules)\n            assertEquals(1L, megajoules)\n            assertEquals(1L, kilojoules)\n            assertEquals(1L, joules)\n            assertEquals(1L, millijoules)\n        }\n    }\n\n    @Test\n    fun infinite_to_international_components() {\n        POSITIVE_INFINITY.millijoules.toInternationalComponents { petajoules, tetrajoules, gigajoules, megajoules, kilojoules, joules, millijoules ->\n            assertEquals(Long.MAX_VALUE, petajoules)\n            assertEquals(Long.MAX_VALUE, tetrajoules)\n            assertEquals(Long.MAX_VALUE, gigajoules)\n            assertEquals(Long.MAX_VALUE, megajoules)\n            assertEquals(Long.MAX_VALUE, kilojoules)\n            assertEquals(Long.MAX_VALUE, joules)\n            assertEquals(Long.MAX_VALUE, millijoules)\n        }\n    }\n\n    @Test\n    fun to_electricity_components_works() {\n        val energy = 1.terawattHours + 1.gigawattHours + 1.megawattHours + 1.kilowattHours + 1.wattHours + 1.milliwattHours + 3.millijoules\n        energy.toElectricityComponents { terawattHours, gigawattHours, megawattHours, kilowattHours, wattHours, milliwattHours, microwattHours ->\n            assertEquals(1L, terawattHours)\n            assertEquals(1L, gigawattHours)\n            assertEquals(1L, megawattHours)\n            assertEquals(1L, kilowattHours)\n            assertEquals(1L, wattHours)\n            assertEquals(1L, milliwattHours)\n            assertEquals(0.8333333333, microwattHours, absoluteTolerance = 0.000001)\n        }\n    }\n\n    @Test\n    fun infinite_to_electricity_components() {\n        POSITIVE_INFINITY.millijoules.toElectricityComponents { terawattHours, gigawattHours, megawattHours, kilowattHours, wattHours, milliwattHours, microwattHours ->\n            assertEquals(Long.MAX_VALUE, terawattHours)\n            assertEquals(Long.MAX_VALUE, gigawattHours)\n            assertEquals(Long.MAX_VALUE, megawattHours)\n            assertEquals(Long.MAX_VALUE, kilowattHours)\n            assertEquals(Long.MAX_VALUE, wattHours)\n            assertEquals(Long.MAX_VALUE, milliwattHours)\n            assertEquals(Double.POSITIVE_INFINITY, microwattHours)\n        }\n    }\n\n    @Test\n    fun to_double_works() {\n        val energy = 12_345.wattHours\n        assertEquals(12.345, energy.toDouble(EnergyUnit.Electricity.KilowattHour))\n    }\n\n    @Test\n    fun to_decial_string_works() {\n        val energy = 12_345.wattHours\n        assertEquals(\"12.345kWh\", energy.toString(EnergyUnit.Electricity.KilowattHour, decimals = 3))\n    }\n\n    @Test\n    fun to_string_picks_correct_energy_display_unit() {\n        val energy = 12_346.joules\n        assertEquals(\"12.35kJ\", energy.toString())\n    }\n\n    @Test\n    fun infinite_energy_div_infinite_duration_throws() {\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY.millijoules / Duration.INFINITE\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY.millijoules / Duration.INFINITE\n        }\n    }\n\n    @Test\n    fun infinite_energy_div_finite_duration_produces_infinite_power() {\n        assertEquals(POSITIVE_INFINITY.microwatts, POSITIVE_INFINITY.millijoules / 10.hours)\n        assertEquals(NEGATIVE_INFINITY.microwatts, NEGATIVE_INFINITY.millijoules / 10.hours)\n    }\n\n    @Test\n    fun finite_energy_div_infinite_duration_produces_zero_power() {\n        assertEquals(0.watts, 1.petajoules / Duration.INFINITE)\n    }\n\n    @Test\n    fun pico_granular_energy_div_micro_granular_time_power_works() {\n        assertEquals(1.watts, 1.joules / 1.seconds)\n    }\n\n    @Test\n    fun nano_granular_energy_div_micro_granular_time_power_works() {\n        assertEquals(1_000_000_000.watts, 1.gigajoules / 1.seconds)\n    }\n\n    @Test\n    fun micro_granular_energy_div_micro_granular_time_power_works() {\n        assertEquals(1_000_000_000_000.watts, 1.tetrajoules / 1.seconds)\n    }\n\n    @Test\n    fun one_microwatt_with_micro_granular_time() {\n        assertEquals(1.microwatts, 1.millijoules / 1_000.seconds)\n    }\n\n    @Test\n    fun one_microwatt_with_milli_granular_time() {\n        assertEquals(1.microwatts, 1_000_000_000_000.millijoules / 1_000_000_000_000_000.seconds)\n    }\n\n    @Test\n    fun energy_div_time_precision_femtojoule_nanosecond() {\n        assertEquals(\n            expected = 1_234.microwatts,\n            actual = 1_234.millijoules / 1_000.seconds,\n        )\n    }\n\n    @Test\n    fun energy_div_time_precision_picojoule_nanosecond() {\n        // 124,999,998,860.937500014 microwatts, but we lose precision.\n        assertEquals(\n            expected = 124_999_998_860.microwatts,\n            actual = 123_456_789.millijoules / 987_654_321.nanoseconds,\n        )\n    }\n\n    @Test\n    fun energy_div_time_precision_nanojoule_nanosecond() {\n        // 124,999,998,860.937500014 microwatts, but we lose precision.\n        assertEquals(\n            expected = 124_999_998_860.microwatts,\n            actual = 123_456_789.joules / 987_654_321.microseconds,\n        )\n    }\n\n    @Test\n    fun energy_div_time_precision_microjoule_nanosecond() {\n        // 124,999,998,860.937500014 microwatts, but we lose precision.\n        assertEquals(\n            expected = 124_999_998_860.microwatts,\n            actual = 123_456_789.kilojoules / 987_654_321.milliseconds,\n        )\n    }\n\n    @Test\n    fun energy_div_time_precision_millijoule_nanosecond() {\n        // 124,999,998,860.937500014 microwatts, but we lose precision.\n        assertEquals(\n            expected = 124_999_998_860.microwatts,\n            actual = 123_456_789.megajoules / 987_654_321.seconds,\n        )\n    }\n\n    @Test\n    fun energy_div_time_precision_nanojoule_millisecond() {\n        // 124.999998861 microwatts, but we lose precision.\n        assertEquals(\n            expected = 124.microwatts,\n            actual = 123_456_789.joules / 987_654_321_000.seconds,\n        )\n    }\n\n    @Test\n    fun energy_div_time_precision_microjoule_millisecond() {\n        // 124,999.998860938 microwatts, but we lose precision.\n        assertEquals(\n            expected = 124_999.microwatts,\n            actual = 123_456_789.kilojoules / 987_654_321_000.seconds,\n        )\n    }\n\n    @Test\n    fun energy_div_time_precision_millijoule_millisecond() {\n        // 124,999,998.860938 microwatts, but we lose precision.\n        assertEquals(\n            expected = 124_999_998.microwatts,\n            actual = 123_456_789.megajoules / 987_654_321_000.seconds,\n        )\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/ForceTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.scalar.millijoules\nimport io.github.kevincianfarini.alchemist.scalar.nanometers\nimport io.github.kevincianfarini.alchemist.scalar.nanonewtons\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\n\nclass ForceTest {\n\n    @Test\n    fun force_mul_length_energy_simple() {\n        assertEquals(\n            expected = 121.millijoules,\n            actual = 123_456_789.nanonewtons * 987_654_321.nanometers,\n        )\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/LengthTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.WasmWasiIgnore\nimport io.github.kevincianfarini.alchemist.internal.NEGATIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.internal.POSITIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.scalar.centimeters\nimport io.github.kevincianfarini.alchemist.scalar.feet\nimport io.github.kevincianfarini.alchemist.scalar.gigameters\nimport io.github.kevincianfarini.alchemist.scalar.inches\nimport io.github.kevincianfarini.alchemist.scalar.kilometers\nimport io.github.kevincianfarini.alchemist.scalar.megameters\nimport io.github.kevincianfarini.alchemist.scalar.meters\nimport io.github.kevincianfarini.alchemist.scalar.micrometers\nimport io.github.kevincianfarini.alchemist.scalar.miles\nimport io.github.kevincianfarini.alchemist.scalar.millimeters\nimport io.github.kevincianfarini.alchemist.scalar.mm2\nimport io.github.kevincianfarini.alchemist.scalar.nanometers\nimport io.github.kevincianfarini.alchemist.scalar.nmPerSecond\nimport io.github.kevincianfarini.alchemist.scalar.yards\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.nanoseconds\nimport kotlin.time.Duration.Companion.seconds\n\nclass LengthTest {\n    \n    @Test\n    fun to_metric_components_works() {\n        val length = 1.gigameters + 1.megameters + 1.kilometers + 1.meters + 1.centimeters + 1.millimeters + 1.micrometers + 1.nanometers\n        length.toInternationalComponents { gm, mega, km, m, cm, milli, um, nm ->\n            assertEquals(1L, gm)\n            assertEquals(1L, mega)\n            assertEquals(1L, km)\n            assertEquals(1L, m)\n            assertEquals(1L, cm)\n            assertEquals(1L, milli)\n            assertEquals(1L, um)\n            assertEquals(1L, nm)\n        }\n    }\n\n    @Test\n    fun to_metric_components_positive_infinity_max_value_every_component() {\n        POSITIVE_INFINITY.nanometers.toInternationalComponents { gm, mega, km, m, cm, milli, um, nm ->\n            assertEquals(Long.MAX_VALUE, gm)\n            assertEquals(Long.MAX_VALUE, mega)\n            assertEquals(Long.MAX_VALUE, km)\n            assertEquals(Long.MAX_VALUE, m)\n            assertEquals(Long.MAX_VALUE, cm)\n            assertEquals(Long.MAX_VALUE, milli)\n            assertEquals(Long.MAX_VALUE, um)\n            assertEquals(Long.MAX_VALUE, nm)\n        }\n    }\n\n    @Test\n    fun to_metric_components_negative_infinity_min_value_every_component() {\n        NEGATIVE_INFINITY.nanometers.toInternationalComponents { gm, mega, km, m, cm, milli, um, nm ->\n            assertEquals(Long.MIN_VALUE, gm)\n            assertEquals(Long.MIN_VALUE, mega)\n            assertEquals(Long.MIN_VALUE, km)\n            assertEquals(Long.MIN_VALUE, m)\n            assertEquals(Long.MIN_VALUE, cm)\n            assertEquals(Long.MIN_VALUE, milli)\n            assertEquals(Long.MIN_VALUE, um)\n            assertEquals(Long.MIN_VALUE, nm)\n        }\n    }\n\n    @Test\n    fun to_imperial_components_works() {\n        val length = 1.miles + 1.yards + 1.feet + 1.inches\n        length.toUnitedStatesCustomaryComponents { miles, yards, feet, inches ->\n            assertEquals(1L, miles)\n            assertEquals(1L, yards)\n            assertEquals(1L, feet)\n            assertEquals(1.0, inches)\n        }\n    }\n\n    @Test\n    fun to_imperial_components_positive_infinity_max_value_every_component() {\n        POSITIVE_INFINITY.nanometers.toUnitedStatesCustomaryComponents { miles, yards, feet, inches ->\n            assertEquals(Long.MAX_VALUE, miles)\n            assertEquals(Long.MAX_VALUE, yards)\n            assertEquals(Long.MAX_VALUE, feet)\n            assertEquals(Double.POSITIVE_INFINITY, inches)\n        }\n    }\n\n    @Test\n    fun to_imperial_components_negative_infinity_min_value_every_component() {\n        NEGATIVE_INFINITY.nanometers.toUnitedStatesCustomaryComponents { miles, yards, feet, inches ->\n            assertEquals(Long.MIN_VALUE, miles)\n            assertEquals(Long.MIN_VALUE, yards)\n            assertEquals(Long.MIN_VALUE, feet)\n            assertEquals(Double.NEGATIVE_INFINITY, inches)\n        }\n    }\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun default_to_string_renders_into_metric_components() {\n        val length = 10_000.meters\n        assertEquals(\n            expected = \"10.00km\",\n            actual = length.toString(),\n        )\n    }\n\n    @Test\n    fun to_string_infinity() {\n        assertEquals(\"Infinity\", POSITIVE_INFINITY.nanometers.toString())\n        assertEquals(\"-Infinity\", NEGATIVE_INFINITY.nanometers.toString())\n    }\n\n    @Test\n    fun to_double_converts_miles_to_km_correctly() {\n        assertEquals(1.609344, 1.miles.toDouble(LengthUnit.International.Kilometer))\n    }\n\n    @Test\n    fun length_mul_length_maximum_nanometers_component_is_lost_to_precision() {\n        // 0.998001998 millimeters², but we lose precision.\n        val nanos = 999_999.nanometers\n        assertEquals(0.mm2, nanos * nanos)\n    }\n\n    @Test\n    fun length_mul_length_gigameters_is_infinite() {\n        assertEquals(POSITIVE_INFINITY.mm2, 1.gigameters * 1.gigameters)\n        assertEquals(NEGATIVE_INFINITY.mm2, 1.gigameters * (-1).gigameters)\n        assertEquals(POSITIVE_INFINITY.mm2, (-1).gigameters * (-1).gigameters)\n        assertEquals(NEGATIVE_INFINITY.mm2, (-1).gigameters * 1.gigameters)\n    }\n\n    @Test\n    fun length_mul_length_megameter_overflow() {\n        assertEquals(POSITIVE_INFINITY.mm2, 100.megameters * 10.megameters)\n    }\n\n    @Test\n    fun length_mul_length_megameter_no_overflow() {\n        assertEquals(\n            expected = 8_000_000_000_000_000_000L.mm2,\n            actual = 4.megameters * 2.megameters\n        )\n    }\n\n    @Test\n    fun length_squared_is_correct() {\n        assertEquals(\n            expected = 1_000_000.mm2,\n            actual = 1.meters.squared(),\n        )\n    }\n\n    @Test\n    fun infinite_length_div_infinite_time_throws() {\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY.nanometers / Duration.INFINITE\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY.nanometers / Duration.INFINITE\n        }\n        assertFailsWith<IllegalArgumentException> {\n            POSITIVE_INFINITY.nanometers / -Duration.INFINITE\n        }\n        assertFailsWith<IllegalArgumentException> {\n            NEGATIVE_INFINITY.nanometers / -Duration.INFINITE\n        }\n    }\n\n    @Test\n    fun infinite_length_div_finite_time() {\n        assertEquals(POSITIVE_INFINITY.nmPerSecond, POSITIVE_INFINITY.nanometers / 1.seconds)\n        assertEquals(NEGATIVE_INFINITY.nmPerSecond, NEGATIVE_INFINITY.nanometers / 1.seconds)\n        assertEquals(NEGATIVE_INFINITY.nmPerSecond, POSITIVE_INFINITY.nanometers / (-1).seconds)\n        assertEquals(POSITIVE_INFINITY.nmPerSecond, NEGATIVE_INFINITY.nanometers / (-1).seconds)\n    }\n\n    @Test\n    fun finite_length_div_infinite_time() {\n        assertEquals(0.nmPerSecond, (Long.MAX_VALUE - 1).nanometers / Duration.INFINITE)\n        assertEquals(0.nmPerSecond, (Long.MAX_VALUE - 1).nanometers / -Duration.INFINITE)\n    }\n\n    @Test\n    fun length_div_time_attometer_nanosecond_precision() {\n        // 124,999,998.8609375 nm/s, but we lose precision.\n        assertEquals(\n            expected = 124_999_998.nmPerSecond,\n            actual = 123_456_789.nanometers / 987_654_321.nanoseconds,\n        )\n    }\n\n    @Test\n    fun length_div_time_femtometer_nanosecond_precision() {\n        // 124,999,998,860.9375 nm/s, but we lose precision.\n        assertEquals(\n            expected = 124_999_998_860.nmPerSecond,\n            actual = 123_456_789.micrometers / 987_654_321.nanoseconds,\n        )\n    }\n\n    @Test\n    fun length_div_time_picometer_nanosecond_precision() {\n        // 124,999,998,860,937.500014 nm/s, but we lose precision.\n        assertEquals(\n            expected = 124_999_998_860_937.nmPerSecond,\n            actual = 123_456_789.millimeters / 987_654_321.nanoseconds,\n        )\n    }\n\n    @Test\n    fun length_div_time_nanometer_nanosecond_precision() {\n        // 124,999,998,860,937,500.014238 nm/s, but we lose precision.\n        assertEquals(\n            expected = 124_999_998_860_937_500.nmPerSecond,\n            actual = 123_456_789.meters / 987_654_321.nanoseconds,\n        )\n    }\n\n    @Test\n    fun length_div_time_picometer_millisecond_precision() {\n        // 124.999998860937500014 nm/s, but we lose precision.\n        assertEquals(\n            expected = 124.nmPerSecond,\n            actual = 123_456_789.millimeters / 987_654_321_000.seconds,\n        )\n    }\n\n    @Test\n    fun length_div_time_nanometer_millisecond_precision() {\n        // 124,999.9988609375 nm/s, but we lose precision.\n        assertEquals(\n            expected = 124_999.nmPerSecond,\n            actual = 123_456_789.meters / 987_654_321_000.seconds,\n        )\n    }\n\n    @Test\n    fun length_div_time_defaults_to_millisecond_precision_if_all_nanosecond_are_infinite() {\n        // 124,999.998860937500014 nm/s, but we lose precision.\n        assertEquals(\n            expected = 124_999.nmPerSecond,\n            actual = 123_456_789.millimeters / 987_654_321.seconds,\n        )\n    }\n\n    @Test\n    fun length_div_time_defaults_to_coarse_nanometers_per_second_when_picometer_remainder_is_infinite() {\n        // 1,000 nm/s, but we lose precision.\n        assertEquals(\n            expected = 0.nmPerSecond,\n            actual = ((Long.MAX_VALUE / 2) - 2).nanometers / ((Long.MAX_VALUE / 2) - 1).milliseconds\n        )\n    }\n\n    @Test\n    fun length_div_velocity_simple() {\n        val length = 10.meters\n        val velocity = 3.meters / 2.seconds\n        assertEquals(6_666_666_666.nanoseconds, length / velocity)\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/PowerTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.internal.NEGATIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.internal.POSITIVE_INFINITY\nimport io.github.kevincianfarini.alchemist.scalar.microwatts\nimport io.github.kevincianfarini.alchemist.scalar.millijoules\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.microseconds\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.nanoseconds\nimport kotlin.time.Duration.Companion.seconds\n\nclass PowerTest {\n\n    @Test\n    fun power_multiply_infinite_time() {\n        assertEquals(\n            expected = POSITIVE_INFINITY.millijoules,\n            actual = 1.microwatts * Duration.INFINITE,\n        )\n        assertEquals(\n            expected = NEGATIVE_INFINITY.millijoules,\n            actual = (-1).microwatts * Duration.INFINITE,\n        )\n        assertEquals(\n            expected = POSITIVE_INFINITY.millijoules,\n            actual = (-1).microwatts * -Duration.INFINITE,\n        )\n        assertEquals(\n            expected = NEGATIVE_INFINITY.millijoules,\n            actual = 1.microwatts * -Duration.INFINITE,\n        )\n    }\n\n    @Test\n    fun infinite_power_multiply_time() {\n        assertEquals(\n            expected = POSITIVE_INFINITY.millijoules,\n            actual = POSITIVE_INFINITY.microwatts * 1.nanoseconds,\n        )\n        assertEquals(\n            expected = POSITIVE_INFINITY.millijoules,\n            actual = NEGATIVE_INFINITY.microwatts * (-1).nanoseconds,\n        )\n        assertEquals(\n            expected = NEGATIVE_INFINITY.millijoules,\n            actual = NEGATIVE_INFINITY.microwatts * 1.nanoseconds,\n        )\n        assertEquals(\n            expected = NEGATIVE_INFINITY.millijoules,\n            actual = POSITIVE_INFINITY.microwatts * (-1).nanoseconds,\n        )\n    }\n\n    @Test\n    fun infinite_power_multiply_infinite_time() {\n        assertEquals(\n            expected = POSITIVE_INFINITY.millijoules,\n            actual = POSITIVE_INFINITY.microwatts * Duration.INFINITE,\n        )\n        assertEquals(\n            expected = POSITIVE_INFINITY.millijoules,\n            actual = NEGATIVE_INFINITY.microwatts * -Duration.INFINITE,\n        )\n        assertEquals(\n            expected = NEGATIVE_INFINITY.millijoules,\n            actual = NEGATIVE_INFINITY.microwatts * Duration.INFINITE,\n        )\n        assertEquals(\n            expected = NEGATIVE_INFINITY.millijoules,\n            actual = POSITIVE_INFINITY.microwatts * -Duration.INFINITE,\n        )\n    }\n\n    @Test\n    fun power_multiply_time_precision_femtojoule_precision() {\n        assertEquals(\n            expected = 1_234.millijoules,\n            actual = 1_234.microwatts * 1_000.seconds\n        )\n    }\n\n    @Test\n    fun power_multiply_time_precision_picojoule_precision() {\n        // 123,456,788.999074074 millijoules, but we lose precision.\n        assertEquals(\n            expected = 123_456_788.millijoules,\n            actual = 124_999_998_860.microwatts * 987_654_321.nanoseconds,\n        )\n    }\n\n    @Test\n    fun power_multiply_time_precision_nanojoule_precision() {\n        // 123,456,788,999.074074 millijoules, but we lose precision.\n        assertEquals(\n            expected = 123_456_788_999.millijoules,\n            actual = 124_999_998_860.microwatts * 987_654_321.microseconds,\n        )\n    }\n\n    @Test\n    fun power_multiply_time_precision_microjoule_precision() {\n        // 123,456,788,999,074.07406 millijoules, but we lose precision.\n        assertEquals(\n            expected = 123_456_788_999_074.millijoules,\n            actual = 124_999_998_860.microwatts * 987_654_321.milliseconds,\n        )\n    }\n\n    @Test\n    fun power_multiply_time_precision_millijoule_precision() {\n        // 123,456,788,999,074,074.06 millijoules, but we lose precision.\n        assertEquals(\n            expected = 123_456_788_999_074_074.millijoules,\n            actual = 124_999_998_860.microwatts * 987_654_321.seconds,\n        )\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/TemperatureTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.WasmWasiIgnore\nimport io.github.kevincianfarini.alchemist.scalar.celsius\nimport io.github.kevincianfarini.alchemist.scalar.fahrenheit\nimport io.github.kevincianfarini.alchemist.scalar.kelvins\nimport io.github.kevincianfarini.alchemist.scalar.nanokelvins\nimport io.github.kevincianfarini.alchemist.unit.TemperatureUnit\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.text.Typography.nbsp\n\nclass TemperatureTest {\n\n    @Test\n    fun well_known_temperatures_are_precise() {\n        assertEquals(0.celsius, 32.fahrenheit)\n        assertEquals(100.celsius, 212.fahrenheit)\n        assertEquals((-40).celsius, (-40).fahrenheit)\n        assertEquals(255_372_222_222.nanokelvins, 0.fahrenheit)\n        assertEquals(273_150_000_000.nanokelvins, 0.celsius)\n    }\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun to_string_with_unit_works_properly() {\n        assertEquals(\"0.00°F\", 0.fahrenheit.toString(TemperatureUnit.Fahrenheit, decimals = 2))\n        assertEquals(\"0.00°C\", 0.celsius.toString(TemperatureUnit.International.Celsius, decimals = 2))\n        assertEquals(\"273.85°C\", 547.kelvins.toString(TemperatureUnit.International.Celsius, decimals = 2))\n        assertEquals(\"0.001${nbsp}μK\", 1.nanokelvins.toString(TemperatureUnit.International.Microkelvin, decimals = 3))\n    }\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun to_string_without_unit_picks_proper_unit() {\n        assertEquals(\"0.00${nbsp}nK\", 0.nanokelvins.toString())\n        assertEquals(\"1.00${nbsp}nK\", 1.nanokelvins.toString())\n        assertEquals(\"1.00${nbsp}μK\", 1_000.nanokelvins.toString())\n        assertEquals(\"1.00${nbsp}mK\", 1_000_000.nanokelvins.toString())\n        assertEquals(\"1.00${nbsp}K\", 1_000_000_000.nanokelvins.toString())\n        assertEquals(\"1.00${nbsp}kK\", 1_000_000_000_000.nanokelvins.toString())\n        assertEquals(\"1.00${nbsp}MK\", 1_000_000_000_000_000.nanokelvins.toString())\n        assertEquals(\"1.00${nbsp}GK\", 1_000_000_000_000_000_000.nanokelvins.toString())\n        assertEquals(\"255.37${nbsp}K\", 0.fahrenheit.toString())\n        assertEquals(\"273.15${nbsp}K\", 0.celsius.toString())\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/VelocityTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.WasmWasiIgnore\nimport io.github.kevincianfarini.alchemist.scalar.meters\nimport io.github.kevincianfarini.alchemist.scalar.nanometers\nimport io.github.kevincianfarini.alchemist.scalar.nmPerSecond2\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.text.Typography.nbsp\nimport kotlin.time.Duration.Companion.hours\nimport kotlin.time.Duration.Companion.nanoseconds\nimport kotlin.time.Duration.Companion.seconds\nimport kotlin.time.DurationUnit\n\nclass VelocityTest {\n\n    @Test\n    fun to_double_simple_works() {\n        assertEquals(\n            expected = 444.4444404,\n            actual = (123_456_789.nanometers / 1.seconds).toDouble(\n                lengthUnit = LengthUnit.International.Meter,\n                durationUnit = DurationUnit.HOURS,\n            ),\n            absoluteTolerance = 0.000001,\n        )\n    }\n\n    @Test\n    fun to_string_with_units_simple_works() {\n        assertEquals(\n            expected = \"3.6${nbsp}km/h\",\n            actual = (1.meters / 1.seconds).toString(\n                lengthUnit = LengthUnit.International.Kilometer,\n                durationUnit = DurationUnit.HOURS,\n                decimals = 1,\n            )\n        )\n    }\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun default_to_string_simple_works() {\n        assertEquals(\n            expected = \"1.00${nbsp}m/s\",\n            actual = (1.meters / 1.seconds).toString()\n        )\n    }\n\n    @Test\n    fun velocity_mul_time_simple() {\n        assertEquals(\n            expected = 1.meters,\n            actual = (1.meters / 1.seconds) * 1.seconds\n        )\n        assertEquals(\n            expected = 444_441_600.meters,\n            actual = (123_456.meters / 1.seconds) * 1.hours,\n        )\n    }\n\n    @Test\n    fun velocity_div_time_simple() {\n        assertEquals(\n            expected = 10_032_520_325.nmPerSecond2,\n            actual = 1_234.nanometers / 1.seconds / 123.nanoseconds,\n        )\n    }\n}"
  },
  {
    "path": "src/commonTest/kotlin/io/github/kevincianfarini/alchemist/type/VolumeTest.kt",
    "content": "package io.github.kevincianfarini.alchemist.type\n\nimport io.github.kevincianfarini.alchemist.WasmWasiIgnore\nimport io.github.kevincianfarini.alchemist.scalar.kiloliters\nimport io.github.kevincianfarini.alchemist.scalar.milliliters\nimport io.github.kevincianfarini.alchemist.unit.LengthUnit\nimport io.github.kevincianfarini.alchemist.unit.VolumeUnit\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.text.Typography.nbsp\n\nclass VolumeTest {\n\n    @Test\n    @WasmWasiIgnore // See: https://youtrack.jetbrains.com/issue/KT-60964.\n    fun to_string_simple() {\n        assertEquals(\n            expected = \"10.00m³\",\n            actual = 10.kiloliters.toString(),\n        )\n    }\n\n    @Test\n    fun to_string_volume_simple() {\n        assertEquals(\n            expected = \"1.234${nbsp}L\",\n            actual = 1_234.milliliters.toString(VolumeUnit.Metric.Liter, decimals = 3),\n        )\n    }\n\n    @Test\n    fun to_double_nanometer_works_as_expected() {\n        assertEquals(\n            expected = 1.234e24,\n            actual = 1_234.milliliters.toDouble(LengthUnit.International.Nanometer),\n        )\n    }\n}"
  },
  {
    "path": "src/jsMain/kotlin/io/github/kevincianfarini/alchemist/internal/double.js.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\nimport kotlin.math.abs\nimport kotlin.math.pow\nimport kotlin.math.round\nimport kotlin.math.sign\nimport kotlin.math.ceil\nimport kotlin.math.log10\n\ninternal actual fun Double.toDecimalString(decimals: Int): String {\n    val rounded = if (decimals == 0) {\n       this\n    } else {\n        val pow = (10.0).pow(decimals)\n        round(abs(this) * pow) / pow * sign(this)\n    }\n    return if (abs(rounded) < 1e21) {\n        // toFixed switches to scientific format after 1e21\n        toFixed(rounded, decimals)\n    } else {\n        // toPrecision outputs the specified number of digits, but only for positive numbers\n        val positive = abs(rounded)\n        val positiveString = toPrecision(positive, ceil(log10(positive)).toInt() + decimals)\n        if (rounded < 0) \"-$positiveString\" else positiveString\n    }\n}\n\n@Suppress(\"UNUSED_PARAMETER\")\nprivate fun toFixed(value: Double, decimals: Int): String =\n    js(\"value.toFixed(decimals)\")\n\n@Suppress(\"UNUSED_PARAMETER\")\nprivate fun toPrecision(value: Double, decimals: Int): String =\n    js(\"value.toPrecision(decimals)\")"
  },
  {
    "path": "src/jsTest/kotlin/io/github/kevincianfarini/alchemist/ignore.js.kt",
    "content": "package io.github.kevincianfarini.alchemist\n\nactual typealias JsIgnore = kotlin.test.Ignore\n"
  },
  {
    "path": "src/jvmMain/kotlin/io/github/kevincianfarini/alchemist/internal/double.jvm.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\nimport java.math.RoundingMode\nimport java.text.DecimalFormat\n\ninternal actual fun Double.toDecimalString(decimals: Int): String {\n    val format = DecimalFormat(\"0\").apply {\n        if (decimals > 0) {\n            minimumFractionDigits = decimals\n            maximumFractionDigits = decimals\n        }\n        roundingMode = RoundingMode.HALF_UP\n    }\n    return format.format(this)\n}"
  },
  {
    "path": "src/nativeMain/kotlin/io/github/kevincianfarini/alchemist/internal/double.native.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\nimport kotlinx.cinterop.ByteVar\nimport kotlinx.cinterop.ExperimentalForeignApi\nimport kotlinx.cinterop.allocArray\nimport kotlinx.cinterop.memScoped\nimport kotlinx.cinterop.toKString\nimport platform.posix.sprintf\n\n@OptIn(ExperimentalForeignApi::class)\ninternal actual fun Double.toDecimalString(decimals: Int): String = memScoped {\n    val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)\n    sprintf(buff, \"%.${decimals}f\", this@toDecimalString)\n    buff.toKString()\n}"
  },
  {
    "path": "src/wasmJsMain/kotlin/io/github/kevincianfarini/alchemist/internal/double.wasmjs.kt",
    "content": "package io.github.kevincianfarini.alchemist.internal\n\nimport kotlin.math.abs\nimport kotlin.math.pow\nimport kotlin.math.round\nimport kotlin.math.sign\nimport kotlin.math.ceil\nimport kotlin.math.log10\n\ninternal actual fun Double.toDecimalString(decimals: Int): String {\n    val rounded = if (decimals == 0) {\n        this\n    } else {\n        val pow = (10.0).pow(decimals)\n        round(abs(this) * pow) / pow * sign(this)\n    }\n    return if (abs(rounded) < 1e21) {\n        // toFixed switches to scientific format after 1e21\n        toFixed(rounded, decimals)\n    } else {\n        // toPrecision outputs the specified number of digits, but only for positive numbers\n        val positive = abs(rounded)\n        val positiveString = toPrecision(positive, ceil(log10(positive)).toInt() + decimals)\n        if (rounded < 0) \"-$positiveString\" else positiveString\n    }\n}\n\n@Suppress(\"UNUSED_PARAMETER\")\nprivate fun toFixed(value: Double, decimals: Int): String =\n    js(\"value.toFixed(decimals)\")\n\n@Suppress(\"UNUSED_PARAMETER\")\nprivate fun toPrecision(value: Double, decimals: Int): String =\n    js(\"value.toPrecision(decimals)\")\n"
  },
  {
    "path": "src/wasmJsTest/kotlin/io/github/kevincianfarini/alchemist/ignore.wasmjs.kt",
    "content": "package io.github.kevincianfarini.alchemist\n\nactual typealias WasmJsIgnore = kotlin.test.Ignore"
  },
  {
    "path": "src/wasmWasiMain/kotlin/io/github/kevincianfarini/alchemist/internal/double.wasmwasi.kt",
    "content": "@file:Suppress(\n    \"LocalVariableName\",\n    \"JoinDeclarationAndAssignment\",\n    \"CanBeVal\",\n    \"FunctionName\",\n    \"LiftReturnOrAssignment\"\n)\n\npackage io.github.kevincianfarini.alchemist.internal\n\nimport kotlin.math.pow\n\ninternal actual fun Double.toDecimalString(decimals: Int): String {\n    // Copy-pasted impl from the stdlib, which has an open issue: (KT-60964).\n    val pow = 10.0.pow(decimals)\n    val round = rint(this * pow)\n    return (round / pow).toString()\n}\n\nprivate val TWO52 = doubleArrayOf(\n    4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */\n    -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */\n)\n\nprivate fun rint(_x: Double): Double {\n    var x: Double = _x\n    var i0: Int\n    var j0: Int\n    var sx: Int\n    var i: UInt\n    var i1: UInt\n    var w: Double\n    var t: Double\n\n    i0 = __HI(x)\n    sx = (i0 shr 31) and 1\n    i1 = __LOu(x)\n    j0 = ((i0 shr 20) and 0x7ff) - 0x3ff\n    if (j0 < 20) {\n        if (j0 < 0) {\n            if (((i0 and 0x7fffffff) or i1.toInt()) == 0) return x\n            i1 = i1 or (i0 and 0x0fffff).toUInt()\n            i0 = i0 and 0xfffe0000.toInt()\n            i0 = i0 or (((i1 or i1.negate()) shr 12) and 0x80000.toUInt()).toInt()\n            x = doubleSetWord(d = x, hi = i0)\n            w = TWO52[sx] + x\n            t = w - TWO52[sx]\n            i0 = __HI(t)\n            t = doubleSetWord(d = t, hi = (i0 and 0x7fffffff) or (sx shl 31))\n            return t\n        } else {\n            i = ((0x000fffff) shr j0).toUInt()\n            if (((i0 and i.toInt()) or i1.toInt()) == 0) return x /* x is integral */\n            i = i shr 1\n            if (((i0 and i.toInt()) or i1.toInt()) != 0) {\n                if (j0 == 19) i1 = 0x40000000.toUInt(); else\n                    i0 = (i0 and i.inv().toInt()) or ((0x20000) shr j0)\n            }\n        }\n    } else if (j0 > 51) {\n        if (j0 == 0x400) return x + x    /* inf or NaN */\n        else return x        /* x is integral */\n    } else {\n        i = ((0xffffffff.toUInt())) shr (j0 - 20)\n        if ((i1 and i) == 0U) return x    /* x is integral */\n        i = i shr 1\n        if ((i1 and i) != 0U) i1 = (i1 and (i.inv())) or ((0x40000000) shr (j0 - 20)).toUInt()\n    }\n    x = doubleSetWord(x, hi = i0, lo = i1.toInt())\n    w = TWO52[sx] + x\n    return w - TWO52[sx]\n}\n\nprivate fun __HI(x: Double): Int = (x.toRawBits() ushr 32).toInt()\n\n//#define __LO(x) *(int*)&x\nprivate fun __LO(x: Double): Int = (x.toRawBits() and 0xFFFFFFFF).toInt()\nprivate fun __LOu(x: Double): UInt = (x.toRawBits() and 0xFFFFFFFF).toUInt()\n\nprivate fun doubleSetWord(d: Double = 0.0, hi: Int = __HI(d), lo: Int = __LO(d)): Double =\n    Double.fromBits((hi.toLong() shl 32) or (lo.toLong() and 0xFFFFFFFF))\n\nprivate fun UInt.negate(): UInt = inv() + 1U\n"
  },
  {
    "path": "src/wasmWasiTest/kotlin/io/github/kevincianfarini/alchemist/ignore.wasmwasi.kt",
    "content": "package io.github.kevincianfarini.alchemist\n\nactual typealias WasmWasiIgnore = kotlin.test.Ignore\n"
  }
]